• VMS x86-64 database server

    From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Sun Jul 6 15:45:13 2025
    As we all know then the available options are currently very limited.

    But I just found a little trick to get one more.

    :-)

    $ write sys$output f$getsyi("VERSION")
    V9.2-3

    $ type TestPG.java
    import java.sql.DriverManager;
    import java.sql.Connection;
    import java.sql.Statement;
    import java.sql.ResultSet;

    public class TestPG {
    public static void main(String[] args) throws Exception {
    Connection con = DriverManager.getConnection("jdbc:postgresql://localhost:5435/test",
    "sa", "hemmeligt");
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT f1,f2 FROM t1");
    while(rs.next()) {
    int f1 = rs.getInt(1);
    String f2 = rs.getString(2);
    System.out.println(f1 + " " + f2);
    }
    rs.close();
    stmt.close();
    con.close();
    }
    }
    $ javac TestPG.java
    $ java -cp .:/javalib/postgresql-42_5_1.jar TestPG
    1 A
    2 BB
    3 CCC

    $ type test.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #include <netdb.h>

    #include <libpq-fe.h>

    int main()
    {
    PGconn *con = PQconnectdb("host=localhost port=5435 dbname=test
    user=sa password=hemmeligt");
    PGresult *res;
    res = PQprepare(con, "stmt_selectf1f2fromt1", "SELECT f1,f2 FROM
    t1", 0, NULL);
    PQclear(res);
    res = PQexecPrepared(con, "stmt_selectf1f2fromt1", 0, NULL, NULL,
    NULL, 0);
    int nrows = PQntuples(res);
    for(int i = 0; i < nrows; i++)
    {
    int f1 = atoi(PQgetvalue(res, i, 0));
    char f2[51];
    strcpy(f2, PQgetvalue(res, i, 1));
    printf("%d %s\n", f1, f2);
    }
    PQclear(res);
    PQexec(con, "DEALLOCATE stmt_selectf1f2fromt1");
    PQfinish(con);
    return 0;
    }
    $ cc /include=libpq$root:[include] /name=as_is test
    $ link test + libpq$root:[lib]libpq/libr +
    libpq$root:[lib]libgpgtypes/libr + ssl111$lib:ssl111$libssl/libr + ssl111$lib:ssl111$libcrypto/libr
    $ run test
    1 A
    2 BB
    3 CCC


    C:\IDEProjects\EclipsePHP\Test>type TestPDO.php
    <?php
    $con = new PDO('pgsql:host=arne4;port=5435;dbname=test', 'sa', 'hemmeligt'); $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    $stmt = $con->prepare('SELECT f1,f2 FROM t1');
    $stmt->execute(array());
    while($row = $stmt->fetch()) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "$f1 $f2\r\n";
    }


    C:\IDEProjects\EclipsePHP\Test>php TestPDO.php
    1 A
    2 BB
    3 CCC

    I cannot use PHP on VMS yet as I don't have a PHP with pgsql and
    pdo_pgsql. But given that libpq is ported, then those can probably
    be build.

    So what is happening here?

    PostgreSQL on VMS x86-64????

    No. Sorry.

    I am using a little trick. H2 can emulate both PostgreSQL
    SQL dialect and PostgreSQL network protocol.

    $ type server.com
    $ java -cp /javalib/h2-2_2_220.jar "org.h2.tools.Server" -tcp
    "-tcpAllowOthers" -pg "-pgAllowOthers" -baseDir .
    $ exit
    $ @server
    TCP server running at tcp://192.168.68.40:9092 (others can connect)
    PG server running at pg://192.168.68.40:5435 (others can connect)

    :-)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Sun Jul 6 18:11:40 2025
    On 7/6/2025 3:45 PM, Arne Vajhøj wrote:
    I am using a little trick. H2 can emulate both PostgreSQL
    SQL dialect and PostgreSQL network protocol.

    To avoid some future frustration: it is necessary to
    create the database and tables in PostgreSQL mode.

    If created normal then trying to access in PostgreSQL mode
    fails due to some differences in handling of case sensitivity.

    So not:

    $ pipe java -cp /javalib/h2-2_2_220.jar "org.h2.tools.Shell" -url "jdbc:h2:./test;FILE_LOCK=FS;" -user sa -password hemmeligt < ddl.sql

    but:

    $ pipe java -cp /javalib/h2-2_2_220.jar "org.h2.tools.Shell" -url "jdbc:h2:./test;FILE_LOCK=FS;MODE=PostgreSQL;DATABASE_TO_LOWER=true"
    -user sa -password hemmeligt < ddl.sql

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Sun Jul 6 18:07:49 2025
    On 7/6/2025 3:45 PM, Arne Vajhøj wrote:
    As we all know then the available options are currently very limited.

    But I just found a little trick to get one more.

    :-)

    $ write sys$output f$getsyi("VERSION")
    V9.2-3

    $ type TestPG.java
    ...
    $ javac TestPG.java
    $ java -cp .:/javalib/postgresql-42_5_1.jar TestPG
    1 A
    2 BB
    3 CCC

    $ type test.c
    ...
    $ cc /include=libpq$root:[include] /name=as_is test
    $ link test + libpq$root:[lib]libpq/libr + libpq$root:[lib]libgpgtypes/
    libr + ssl111$lib:ssl111$libssl/libr + ssl111$lib:ssl111$libcrypto/libr
    $ run test
    1 A
    2 BB
    3 CCC

    C:\IDEProjects\EclipsePHP\Test>type TestPDO.php
    ...
    C:\IDEProjects\EclipsePHP\Test>php TestPDO.php
    1 A
    2 BB
    3 CCC

    I cannot use PHP on VMS yet as I don't have a PHP with pgsql and
    pdo_pgsql. But given that libpq is ported, then those can probably
    be build.

    About the same with Python. Trying to install psycopg2 with
    pip fails, but given that VSI provide libpq, then I assume
    that someone with Python+C+VMS skills could build it.

    It is worse with embedded C.

    VSI libpq port does include ecpg and it works and the
    result can connect to H2 just fine.

    But there are some incompatibilities between H2 in
    PostgreSQL mode and real PostgreSQL.

    And one of them is that H2 in PostgreSQL mode
    does not support server side cursors.

    And embedded SQL without "FETCH cursorname INTO ..."
    is like a hotdog without a hotdog sausage!

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sun Jul 6 21:39:45 2025
    On Sun, 6 Jul 2025 15:45:13 -0400, Arne Vajhøj wrote:

    I cannot use PHP on VMS yet ...

    You’re not losing much.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Sun Jul 6 17:39:45 2025
    On 7/6/25 2:45 PM, Arne Vajhøj wrote:

    So what is happening here?

    PostgreSQL on VMS x86-64????

    No. Sorry.

    I am using a little trick. H2 can emulate both PostgreSQL
    SQL dialect and PostgreSQL network protocol.

    As well as others, apparently. Did you have existing code, tooling, or experience that made the emulated route more attractive than just using
    H2's own ODBC/JDBC interfaces?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Sun Jul 6 19:57:01 2025
    On 7/6/2025 6:39 PM, Craig A. Berry wrote:
    On 7/6/25 2:45 PM, Arne Vajhøj wrote:
    So what is happening here?

    PostgreSQL on VMS x86-64????

    No. Sorry.

    I am using a little trick. H2 can emulate both PostgreSQL
    SQL dialect and PostgreSQL network protocol.

    As well as others, apparently.

    It can emulate a lot of different SQL dialects.

    But only PostgreSQL network protocol I believe. Which
    is important, because that is what allows usage
    of a PostgreSQL client library.

      Did you have existing code, tooling, or experience that made the emulated route more attractive than just using
    H2's own ODBC/JDBC interfaces?

    The trick is not interesting for Java and other JVM languages as
    the JDBC driver is available.

    It is interesting for languages where there are no H2 driver
    available but a PostgreSQL driver available.

    On VMS that means C out of the box via VSI provided libpq.

    But it also means that other native languages (Pascal, Basic,
    Fortran etc.) just need a wrapper to use libpq to talk to
    H2.

    And that PHP pgsql & pdo_pgsql and Python psychopg2
    should be just a build problem.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Sun Jul 6 19:58:04 2025
    On 7/6/2025 7:19 PM, Lawrence D'Oliveiro wrote:
    On Sun, 6 Jul 2025 18:07:49 -0400, Arne Vajhøj wrote:
    And embedded SQL without "FETCH cursorname INTO ..." is like a
    hotdog without a hotdog sausage!

    Never used FETCH before -- had to look it up. MariaDB/MySQL has it, SQLite doesn’t.

    It is never used in call API's or in ORM's on top of call API's.

    But in embedded SQL then it is the standard way to do queries.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sun Jul 6 23:19:34 2025
    On Sun, 6 Jul 2025 18:07:49 -0400, Arne Vajhøj wrote:

    And embedded SQL without "FETCH cursorname INTO ..." is like a
    hotdog without a hotdog sausage!

    Never used FETCH before -- had to look it up. MariaDB/MySQL has it, SQLite doesn’t.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Mon Jul 7 02:42:14 2025
    On Sun, 6 Jul 2025 19:58:04 -0400, Arne Vajhøj wrote:

    But in embedded SQL then it is the standard way to do queries.

    Not sure what “embedded SQL” means. I normally use SQL “embedded” in an app written in some other programming language.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mark Berryman@21:1/5 to All on Mon Jul 7 08:50:05 2025
    On 7/6/25 5:57 PM, Arne Vajhøj wrote:
    .
    .
    And that PHP pgsql & pdo_pgsql and Python psychopg2
    should be just a build problem.

    $ php -i
    .
    .
    .
    PDO
    PDO support => enabled
    PDO drivers => mysql, pgsql, sqlite
    pdo_mysql
    PDO Driver for MySQL => enabled
    pdo_pgsql
    PDO Driver for PostgreSQL => enabled
    pdo_sqlite
    PDO Driver for SQLite 3.x => enabled

    pgsql
    pgsql.allow_persistent => On => On
    pgsql.auto_reset_persistent => Off => Off
    pgsql.ignore_notice => Off => Off
    pgsql.log_notice => Off => Off
    pgsql.max_links => Unlimited => Unlimited
    pgsql.max_persistent => Unlimited => Unlimited


    My PHP port has pgsql and pdo_pgsql support using the VSI libpq library.

    Mark Berryman

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jul 7 14:07:31 2025
    On 7/6/2025 10:42 PM, Lawrence D'Oliveiro wrote:
    On Sun, 6 Jul 2025 19:58:04 -0400, Arne Vajhøj wrote:
    But in embedded SQL then it is the standard way to do queries.

    Not sure what “embedded SQL” means. I normally use SQL “embedded” in an
    app written in some other programming language.

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed
    by EXEC SQL, put it through a pre-compiler to get valid
    code in whatever language (Cobol, PL/I, C or whatever).

    Here is C call API code:

    PGresult *res;
    res = PQprepare(con, "stmt_selectf1f2fromt1", "SELECT f1,f2 FROM
    t1", 0, NULL);
    PQclear(res);
    res = PQexecPrepared(con, "stmt_selectf1f2fromt1", 0, NULL, NULL,
    NULL, 0);
    int nrows = PQntuples(res);
    for(int i = 0; i < nrows; i++)
    {
    int f1 = atoi(PQgetvalue(res, i, 0));
    char f2[51];
    strcpy(f2, PQgetvalue(res, i, 1));
    printf("%d %s\n", f1, f2);
    }
    PQclear(res);
    PQexec(con, "DEALLOCATE stmt_selectf1f2fromt1");

    same code as C with embedded SQL:

    EXEC SQL DECLARE mycursor CURSOR FOR SELECT f1,f2 FROM t1;
    EXEC SQL OPEN mycursor;
    for(;;)
    {
    EXEC SQL FETCH mycursor INTO :f1, :f2;
    if(sqlca.sqlcode != 0) break;
    printf("%d %s\n", f1, f2);
    }
    EXEC SQL CLOSE mycursor;

    Simpler shorter code, because the pre-compiler handle
    some of the plumbing.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Mark Berryman on Mon Jul 7 14:01:31 2025
    On 7/7/2025 10:50 AM, Mark Berryman wrote:
    On 7/6/25 5:57 PM, Arne Vajhøj wrote:
    And that PHP pgsql & pdo_pgsql and Python psychopg2
    should be just a build problem.

    $ php -i
    .
    .
    .
    PDO
    PDO support => enabled
    PDO drivers => mysql, pgsql, sqlite
    pdo_mysql
    PDO Driver for MySQL => enabled
    pdo_pgsql
    PDO Driver for PostgreSQL => enabled
    pdo_sqlite
    PDO Driver for SQLite 3.x => enabled

    pgsql
    pgsql.allow_persistent => On => On
    pgsql.auto_reset_persistent => Off => Off
    pgsql.ignore_notice => Off => Off
    pgsql.log_notice => Off => Off
    pgsql.max_links => Unlimited => Unlimited
    pgsql.max_persistent => Unlimited => Unlimited

    My PHP port has pgsql and pdo_pgsql support using the VSI libpq library.

    I do use your PHP.

    But I do not have pgsql and pdo_pgsql loaded.

    $ pipe php -i | search sys$input php,version/match=and
    PHP Version => 8.1.23
    $ pipe php -i | search sys$input pdo
    ...
    PDO
    PDO support => enabled
    PDO drivers => mysql
    pdo_mysql
    PDO Driver for MySQL => enabled
    ...

    The funny thing is that unlike most other extensions
    then PHP_PGSQL.EXE and PHP_PDO_PGSQL.EXE exist
    as seperate files. But even if I try to load them in
    php.ini, then nothing.

    I must be missing something. Probably something basic,
    but ...

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Mon Jul 7 14:21:58 2025
    On 7/7/2025 2:16 PM, Arne Vajhøj wrote:
    On 7/7/2025 2:07 PM, Arne Vajhøj wrote:
    On 7/6/2025 10:42 PM, Lawrence D'Oliveiro wrote:
    On Sun, 6 Jul 2025 19:58:04 -0400, Arne Vajhøj wrote:
    But in embedded SQL then it is the standard way to do queries.

    Not sure what “embedded SQL” means. I normally use SQL “embedded” in an
    app written in some other programming language.

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed
    by EXEC SQL, put it through a pre-compiler to get valid
    code in whatever language (Cobol, PL/I, C or whatever).

    I believe the available pre-compilers (database and language combos)
    for VMS are:

    Oracle DB - Cobol, C, Fortran [support is ended, will never be on x86-64] Oracle Rdb - Cobol, C, Pascal, Fortran [not on x86-64 yet but coming]
    Mimer - Cobol, C, Fortran
    ? via SQLRelay - Cobol, C
    any database with JDBC driver - Java [unsupported]

    +the topic

    PgSQL - C

    :-)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Mon Jul 7 14:16:52 2025
    On 7/7/2025 2:07 PM, Arne Vajhøj wrote:
    On 7/6/2025 10:42 PM, Lawrence D'Oliveiro wrote:
    On Sun, 6 Jul 2025 19:58:04 -0400, Arne Vajhøj wrote:
    But in embedded SQL then it is the standard way to do queries.

    Not sure what “embedded SQL” means. I normally use SQL “embedded” in an
    app written in some other programming language.

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed
    by EXEC SQL, put it through a pre-compiler to get valid
    code in whatever language (Cobol, PL/I, C or whatever).

    I believe the available pre-compilers (database and language combos)
    for VMS are:

    Oracle DB - Cobol, C, Fortran [support is ended, will never be on x86-64] Oracle Rdb - Cobol, C, Pascal, Fortran [not on x86-64 yet but coming]
    Mimer - Cobol, C, Fortran
    ? via SQLRelay - Cobol, C
    any database with JDBC driver - Java [unsupported]

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mark Berryman@21:1/5 to All on Mon Jul 7 15:06:24 2025
    On 7/7/25 12:01 PM, Arne Vajhøj wrote:
    On 7/7/2025 10:50 AM, Mark Berryman wrote:
    On 7/6/25 5:57 PM, Arne Vajhøj wrote:
    And that PHP pgsql & pdo_pgsql and Python psychopg2
    should be just a build problem.

    $ php -i
    .
    .
    .
    PDO
    PDO support => enabled
    PDO drivers => mysql, pgsql, sqlite
    pdo_mysql
    PDO Driver for MySQL => enabled
    pdo_pgsql
    PDO Driver for PostgreSQL => enabled
    pdo_sqlite
    PDO Driver for SQLite 3.x => enabled

    pgsql
    pgsql.allow_persistent => On => On
    pgsql.auto_reset_persistent => Off => Off
    pgsql.ignore_notice => Off => Off
    pgsql.log_notice => Off => Off
    pgsql.max_links => Unlimited => Unlimited
    pgsql.max_persistent => Unlimited => Unlimited

    My PHP port has pgsql and pdo_pgsql support using the VSI libpq library.

    I do use your PHP.

    But I do not have pgsql and pdo_pgsql loaded.

    $ pipe php -i | search sys$input php,version/match=and
    PHP Version => 8.1.23
    $ pipe php -i | search sys$input pdo
    ...
    PDO
    PDO support => enabled
    PDO drivers => mysql
    pdo_mysql
    PDO Driver for MySQL => enabled
    ...

    The funny thing is that unlike most other extensions
    then PHP_PGSQL.EXE and PHP_PDO_PGSQL.EXE exist
    as seperate files. But even if I try to load them in
    php.ini, then nothing.

    I must be missing something. Probably something basic,
    but ...

    Some of the extensions were built as separate loadable modules either
    due to their size or because they require separate optional libraries
    that not everyone has. All that is necessary to load them is to have
    the following in php_root:[000000]php.ini:

    extension=php_pdo_pgsql.exe
    extension=php_pgsql.exe

    and to make sure that php.ini is readable by the account running php
    (and, of course, for libpq from VSI to be installed).

    If that still doesn't work then try the following:

    $ php -na
    php > echo dl("php_pgsql.exe");
    php >

    If that returns a 1, you have successfully loaded the extension and
    there is something wrong with your php.ini file. If it returns a 0, you
    will get an error message telling you why the extension could not be
    loaded.


    Mark Berryman

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Mark Berryman on Mon Jul 7 17:36:03 2025
    On 7/7/2025 5:06 PM, Mark Berryman wrote:
    On 7/7/25 12:01 PM, Arne Vajhøj wrote:
    I do use your PHP.

    But I do not have pgsql and pdo_pgsql loaded.

    $ pipe php -i | search sys$input php,version/match=and
    PHP Version => 8.1.23
    $ pipe php -i | search sys$input pdo
    ...
    PDO
    PDO support => enabled
    PDO drivers => mysql
    pdo_mysql
    PDO Driver for MySQL => enabled
    ...

    The funny thing is that unlike most other extensions
    then PHP_PGSQL.EXE and PHP_PDO_PGSQL.EXE exist
    as seperate files. But even if I try to load them in
    php.ini, then nothing.

    I must be missing something. Probably something basic,
    but ...

    Some of the extensions were built as separate loadable modules either
    due to their size or because they require separate optional libraries
    that not everyone has.  All that is necessary to load them is to have
    the following in php_root:[000000]php.ini:

    extension=php_pdo_pgsql.exe
    extension=php_pgsql.exe

    and to make sure that php.ini is readable by the account running php
    (and, of course, for libpq from VSI to be installed).

    First I did not have anything for pgsql in php.ini
    and second I tried without php_ prefix.

    Loading with php_ prefix and everything works!

    Thanks.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Mon Jul 7 17:37:00 2025
    On 7/7/2025 5:36 PM, Arne Vajhøj wrote:
    On 7/7/2025 5:06 PM, Mark Berryman wrote:
    On 7/7/25 12:01 PM, Arne Vajhøj wrote:
    I do use your PHP.

    But I do not have pgsql and pdo_pgsql loaded.

    $ pipe php -i | search sys$input php,version/match=and
    PHP Version => 8.1.23
    $ pipe php -i | search sys$input pdo
    ...
    PDO
    PDO support => enabled
    PDO drivers => mysql
    pdo_mysql
    PDO Driver for MySQL => enabled
    ...

    The funny thing is that unlike most other extensions
    then PHP_PGSQL.EXE and PHP_PDO_PGSQL.EXE exist
    as seperate files. But even if I try to load them in
    php.ini, then nothing.

    I must be missing something. Probably something basic,
    but ...

    Some of the extensions were built as separate loadable modules either
    due to their size or because they require separate optional libraries
    that not everyone has.  All that is necessary to load them is to have
    the following in php_root:[000000]php.ini:

    extension=php_pdo_pgsql.exe
    extension=php_pgsql.exe

    and to make sure that php.ini is readable by the account running php
    (and, of course, for libpq from VSI to be installed).

    First I did not have anything for pgsql in php.ini
    and second I tried without php_ prefix.

    Loading with php_ prefix and everything works!

    $ type TestPDO.php
    <?php
    $con = new PDO('pgsql:host=localhost;port=5435;dbname=test', 'sa', 'hemmeligt');
    $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    $stmt = $con->prepare('SELECT f1,f2 FROM t1');
    $stmt->execute(array());
    while($row = $stmt->fetch()) {
    $f1 = $row['f1'];
    $f2 = $row['f2'];
    echo "$f1 $f2\r\n";
    }

    $ php TestPDO.php
    1 A
    2 BB
    3 CCC

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Mon Jul 7 22:07:13 2025
    On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).

    OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with
    dynamic strings, to make it easier for them to compose SQL statements.

    And the horribleness of it is reinforced by your statement that you need server-side cursors in the DBMS to make this hack a little less difficult
    to work with.

    Not sure why it would be needed for PL/I, or even C.

    I’ve pointed out before, the irony of COBOL being designed strictly for “business” needs, yet within a couple of decades of its widespread adoption, SQL databases became an important “business” need, but the best way of interfacing to them (using dynamic string manipulation mechanisms)
    had been omitted from COBOL because it wasn’t seen as a “business” need.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Mark Berryman on Mon Jul 7 22:08:51 2025
    On Mon, 7 Jul 2025 15:06:24 -0600, Mark Berryman wrote:

    Some of the extensions were built as separate loadable modules either
    due to their size or because they require separate optional libraries
    that not everyone has. All that is necessary to load them is to have
    the following in php_root:[000000]php.ini:

    extension=php_pdo_pgsql.exe
    extension=php_pgsql.exe

    Why did VMS ever adopt the convention that shareable libraries would have
    tne same .EXE extension as executables?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jul 7 19:28:37 2025
    On 7/7/2025 6:07 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:
    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).

    OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with dynamic strings, to make it easier for them to compose SQL statements.

    Not really.

    The code saved is usually not string manipulation code but
    calls to associate SQL columns or parameters with host
    variables.

    And the horribleness of it is reinforced by your statement that you need server-side cursors in the DBMS to make this hack a little less difficult
    to work with.

    Embedded SQL (except for Java) expect sever side cursors. Just
    a prerequisite.

    Not sure why it would be needed for PL/I, or even C.

    The benefits really apply to those just as well as Cobol.

    Just look at the C examples I posted.

    I’ve pointed out before, the irony of COBOL being designed strictly for “business” needs, yet within a couple of decades of its widespread adoption, SQL databases became an important “business” need, but the best way of interfacing to them (using dynamic string manipulation mechanisms)
    had been omitted from COBOL because it wasn’t seen as a “business” need.

    Cobol access relational databases fine. Probably because dynamic
    string manipulation is rarely used for database access. It is a
    code smell.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jul 7 19:21:09 2025
    On 7/7/2025 6:08 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 15:06:24 -0600, Mark Berryman wrote:
    Some of the extensions were built as separate loadable modules either
    due to their size or because they require separate optional libraries
    that not everyone has. All that is necessary to load them is to have
    the following in php_root:[000000]php.ini:

    extension=php_pdo_pgsql.exe
    extension=php_pgsql.exe

    Why did VMS ever adopt the convention that shareable libraries would have
    tne same .EXE extension as executables?

    Maybe because someone at DEC made a random decision in 1977?

    Maybe because they practically are the same?

    $ type main1.c
    #include <stdio.h>

    void f(const char *s)
    {
    puts(s);
    }

    int main()
    {
    f("main1");
    return 0;
    }
    $ cc main1
    $ link/share main1 + sys$input/opt
    SYMBOL_VECTOR=(f=PROCEDURE)
    $
    $ run main1
    main1
    $ define/nolog main1shr sys$disk:[]main1
    $ type main2.c
    #include <stdio.h>

    void f(const char *s);

    int main()
    {
    f("main2");
    return 0;
    }
    $ cc main2
    $ link main2 + sys$input/opt
    main1shr/share
    $
    $ run main2
    main2

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 00:27:14 2025
    On Mon, 7 Jul 2025 19:21:09 -0400, Arne Vajhøj wrote:

    Maybe because they practically are the same?

    If they need to be distinguished, then they should have been given
    different extensions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 00:26:37 2025
    On Mon, 7 Jul 2025 19:28:37 -0400, Arne Vajhøj wrote:

    ... dynamic string manipulation is rarely used for database access. It
    is a code smell.

    I posted examples some years ago in this group about how useful they are.
    Want to revisit those?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Louis Krupp@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 02:14:35 2025
    This is a multi-part message in MIME format.
    On 7/7/2025 4:07 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).
    OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with dynamic strings, to make it easier for them to compose SQL statements.

    And the horribleness of it is reinforced by your statement that you need server-side cursors in the DBMS to make this hack a little less difficult
    to work with.

    Not sure why it would be needed for PL/I, or even C.

    I’ve pointed out before, the irony of COBOL being designed strictly for “business” needs, yet within a couple of decades of its widespread adoption, SQL databases became an important “business” need, but the best way of interfacing to them (using dynamic string manipulation mechanisms)
    had been omitted from COBOL because it wasn’t seen as a “business” need.

    Coding in COBOL has always involved certain sacrifices.

    As for embedded SQL being nonstandard, I think you might be interested
    in this page:

    https://www.iso.org/standard/84805.html

    which contains this line:

    ISO/IEC 9075-2 specifies embedded SQL for the programming languages:
    Ada, C, COBOL, Fortran, MUMPS, Pascal, and PL/I.

    Louis


    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
    <div class="moz-cite-prefix">On 7/7/2025 4:07 PM, Lawrence
    D'Oliveiro wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:104hgeh$3474l$[email protected]">
    <pre wrap="" class="moz-quote-pre">On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:

    </pre>
    <blockquote type="cite">
    <pre wrap="" class="moz-quote-pre">Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).
    </pre>
    </blockquote>
    <pre wrap="" class="moz-quote-pre">OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with
    dynamic strings, to make it easier for them to compose SQL statements.

    And the horribleness of it is reinforced by your statement that you need server-side cursors in the DBMS to make this hack a little less difficult
    to work with.

    Not sure why it would be needed for PL/I, or even C.

    I’ve pointed out before, the irony of COBOL being designed strictly for “business” needs, yet within a couple of decades of its widespread adoption, SQL databases became an important “business” need, but the best way of interfacing to them (using dynamic string manipulation mechanisms)
    had been omitted from COBOL because it wasn’t seen as a “business” need. </pre>
    </blockquote>
    <br>
    Coding in COBOL has always involved certain sacrifices.<br>
    <br>
    As for embedded SQL being nonstandard, I think you might be
    interested in this page:<br>
    <br>
    <a class="moz-txt-link-freetext"
    href="https://www.iso.org/standard/84805.html"
    moz-do-not-send="true">https://www.iso.org/standard/84805.html</a><br>
    <br>
    which contains this line:<br>
    <blockquote>ISO/IEC 9075-2 specifies embedded SQL for the programming
    languages: Ada, C, COBOL, Fortran, MUMPS, Pascal, and PL/I.<br>
    </blockquote>
    Louis<br>
    <br>
    <br>
    </body>
    </html>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 07:14:44 2025
    On 7/7/2025 8:27 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 19:21:09 -0400, Arne Vajhøj wrote:

    Maybe because they practically are the same?

    If they need to be distinguished, then they should have been given
    different extensions.

    $ run main1
    main1
    ...
    $ define/nolog main1shr sys$disk:[]main1
    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 08:45:13 2025
    On 7/7/2025 8:26 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 19:28:37 -0400, Arne Vajhøj wrote:
    ... dynamic string manipulation is rarely used for database access. It
    is a code smell.

    I posted examples some years ago in this group about how useful they are. Want to revisit those?

    You were also told how it should have been done in Python and
    how it would be done in Cobol.

    Cobol support dynamic strings fine. But it is not a good
    choice for SQL.

    For security reasons (and possible for performance reasons).

    $ type sql.py
    v1 = 123
    v2 = "ABC'); DROP TABLE importantdata; --"
    sql = f"INSERT INTO data VALUES({v1},'{v2}')"
    print(sql)
    $ python sql.py
    INSERT INTO data VALUES(123,'ABC'); DROP TABLE importantdata; --')
    $ type sql.cob
    identification division.
    program-id.sqlprg.
    *
    data division.
    working-storage section.
    01 v1 pic 9(9) value 123.
    01 v2 pic x(80) value "ABC'); DROP TABLE importantdata; --".
    01 sql pic x(80).
    *
    procedure division.
    main-paragraph.
    string "INSERT INTO data VALUES(" v1 ",'" v2 "')" delimited by size
    into sql
    display sql
    stop run.
    $ cob sql
    $ lin sql
    $ r sql
    INSERT INTO data VALUES(000000123,'ABC'); DROP TABLE importantdata; --

    Of course the Python code is still a lot shorter than the
    Cobol code, but that is generally the case.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Louis Krupp on Tue Jul 8 09:35:16 2025
    On 7/8/2025 4:14 AM, Louis Krupp wrote:
    On 7/7/2025 4:07 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:
    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).
    OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with
    dynamic strings, to make it easier for them to compose SQL statements.

    And the horribleness of it is reinforced by your statement that you need
    server-side cursors in the DBMS to make this hack a little less difficult
    to work with.

    Not sure why it would be needed for PL/I, or even C.

    As for embedded SQL being nonstandard, I think you might be interested
    in this page:

    https://www.iso.org/standard/84805.html

    which contains this line:

    ISO/IEC 9075-2 specifies embedded SQL for the programming languages:
    Ada, C, COBOL, Fortran, MUMPS, Pascal, and PL/I.

    Note that while -2 covers native languages, then -10 cover
    Java.

    I am surprised that they still maintain embedded SQL for Java.
    SQLJ has been out of fashion for like 25 years.

    It can still be made to work though. Also on VMS.

    Demo with SQLite:

    $ type Test.sqlj
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;

    #sql context ConCtx;

    public class Test {
    private String constr;
    private String usr;
    private String pwd;
    private ConCtx getContext() throws SQLException {
    return new ConCtx(DriverManager.getConnection(constr, usr, pwd));
    }
    public Test(String constr, String usr, String pwd) {
    this.constr = constr;
    this.usr = usr;
    this.pwd = pwd;
    }
    #sql private static iterator T1Iterator(int, String);
    public T1 getOne(int f1) throws SQLException {
    T1 res;
    ConCtx ctx = getContext();
    T1Iterator it;
    #sql [ctx] it = { SELECT f1,f2 FROM t1 WHERE f1 = :f1};
    int xf1 = 0;
    String xf2 = null;
    #sql { fetch :it INTO :xf1, :xf2 };
    if(!it.endFetch()) {
    res = new T1(xf1, xf2);
    } else {
    res = null;
    }
    ctx.close();
    return res;
    }
    public List<T1> getAll() throws SQLException {
    List<T1> res = new ArrayList<T1>();
    ConCtx ctx = getContext();
    T1Iterator it;
    #sql [ctx] it = { SELECT f1,f2 FROM t1 };
    while(true) {
    int xf1 = 0;
    String xf2 = null;
    #sql { fetch :it INTO :xf1, :xf2 };
    if(it.endFetch()) break;
    res.add(new T1(xf1, xf2));
    }
    ctx.close();
    return res;
    }
    public void save(T1 o) throws SQLException {
    ConCtx ctx = getContext();
    int f1 = o.getF1();
    String f2 = o.getF2();
    #sql [ctx] { INSERT INTO t1 VALUES(:f1,:f2) };
    ctx.close();
    }
    public void remove(int f1) throws SQLException {
    ConCtx ctx = getContext();
    #sql [ctx] { DELETE FROM t1 WHERE f1 = :f1 };
    ctx.close();
    }
    }
    $ type T1.java
    public class T1 {
    private int f1;
    private String f2;
    public T1() {
    this(0, "");
    }
    public T1(int f1, String f2) {
    this.f1 = f1;
    this.f2 = f2;
    }
    public int getF1() {
    return f1;
    }
    public void setF1(int f1) {
    this.f1 = f1;
    }
    public String getF2() {
    return f2;
    }
    public void setF2(String f2) {
    this.f2 = f2;
    }
    public String toString() {
    return String.format("[%d,%s]", f1, f2);
    }
    }
    $ java -cp translator.jar:runtime12.jar:ojdbc5.jar:sunio.jar:sqlite-jdbc-3_47_2_0.jar "sqlj.tools.Sqlj" -codegen=iso -compile=false -driver="org.sqlite.JDBC" -url="jdbc:sqlite:test.db" -user="" -password="" "Test.sqlj"
    $ javac -cp runtime12.jar Main.java Test.java T1.java
    $ java "-Xmx512m" -cp .:translator.jar:runtime12.jar:ojdbc5.jar:sunio.jar:sqlite-jdbc-3_47_2_0.jar "Main" "org.sqlite.JDBC" "jdbc:sqlite:test.db" "" ""
    [2,BB]
    [1,A]
    [2,BB]
    [3,CCC]
    [1,A]
    [2,BB]
    [3,CCC]
    [999,XXX]
    [1,A]
    [2,BB]
    [3,CCC]

    runtime12.jar and translator.jar is Oracle SQLJ implementation
    from many years ago. They can be found in an older Oracle DB
    or Oracle DB client kit.

    ojdbc5.jar is a standard Oracle JDBC driver for Java 5. Oracle
    SQLJ supports other databases than Oracle DB, but still require
    Oracle DB JDBC driver.

    sunio.jar is the sun.io package from a Java 5/6/7. That package
    is unsupported, not supposed to be used and removed in Java 8.
    But SQLJ uses it. So one has to borrow those classes from an
    older Java.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 21:56:05 2025
    On Tue, 8 Jul 2025 09:35:16 -0400, Arne Vajhøj wrote:

    On 7/8/2025 4:14 AM, Louis Krupp wrote:

    On 7/7/2025 4:07 PM, Lawrence D'Oliveiro wrote:

    On Mon, 7 Jul 2025 14:07:31 -0400, Arne Vajhøj wrote:

    Embedded SQL is a thing or was a thing 30-40-50 years ago.

    Basically you write source code with SQL statements prefixed by EXEC
    SQL, put it through a pre-compiler to get valid code in whatever
    language (Cobol, PL/I, C or whatever).

    OK, so it was a horrible nonstandard hack invented for programming
    languages like COBOL, which didn’t have the best ability to deal with
    dynamic strings, to make it easier for them to compose SQL statements.

    And the horribleness of it is reinforced by your statement that you
    need server-side cursors in the DBMS to make this hack a little less
    difficult to work with.

    As for embedded SQL being nonstandard, I think you might be interested
    in this page:

    https://www.iso.org/standard/84805.html

    which contains this line:

    ISO/IEC 9075-2 specifies embedded SQL for the programming languages:
    Ada, C, COBOL, Fortran, MUMPS, Pascal, and PL/I.

    Note that while -2 covers native languages, then -10 cover Java.

    I am surprised that they still maintain embedded SQL for Java.
    SQLJ has been out of fashion for like 25 years.

    Yes, but “out of fashion” or not, it’s still supposed to be an official standard according to ISO 9057, isn’t it? Otherwise we could apply the
    “out of fashion” argument to some or all of those other parts of the spec as well.

    Either that, or you have to conclude that trying to patch up deficiencies
    in one particular collection of official language specs by creating a
    whole separate add-on spec that applies to that set of languages is not
    exactly a scaleable endeavour.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 21:57:27 2025
    On Tue, 8 Jul 2025 08:45:13 -0400, Arne Vajhøj wrote:

    On 7/7/2025 8:26 PM, Lawrence D'Oliveiro wrote:

    On Mon, 7 Jul 2025 19:28:37 -0400, Arne Vajhøj wrote:

    ... dynamic string manipulation is rarely used for database access. It
    is a code smell.

    I posted examples some years ago in this group about how useful they
    are. Want to revisit those?

    You were also told how it should have been done in Python and how it
    would be done in Cobol.

    Except the alternative Python versions didn’t actually work.

    I can’t decide whether there is actually widespread fear about the possibilities of dynamically-generated SQL, or just a lack of imagination.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 21:58:49 2025
    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:

    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified directly
    on the command line, without having to go through options files. Why does
    the VMS linker need options files for this common case?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 18:14:32 2025
    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:
    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified directly
    on the command line, without having to go through options files. Why does
    the VMS linker need options files for this common case?

    /SHARE in command line means producing a shareable image.

    /SHARE in option file means linking with a shareable image.

    Different meaning based on context.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 18:40:31 2025
    On 7/8/2025 5:57 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 08:45:13 -0400, Arne Vajhøj wrote:
    On 7/7/2025 8:26 PM, Lawrence D'Oliveiro wrote:
    On Mon, 7 Jul 2025 19:28:37 -0400, Arne Vajhøj wrote:
    ... dynamic string manipulation is rarely used for database access. It >>>> is a code smell.

    I posted examples some years ago in this group about how useful they
    are. Want to revisit those?

    You were also told how it should have been done in Python and how it
    would be done in Cobol.

    Except the alternative Python versions didn’t actually work.

    It worked. It was tested before posted. Cobol embedded SQL was
    tested with Rdb and Python was tested with SQLite.

    I can’t decide whether there is actually widespread fear about the possibilities of dynamically-generated SQL, or just a lack of imagination.

    Dynamically creating SQL string where the dynamic part is for non-data
    is rarely needed. The COALESCE trick handle many of the potential cases.

    Dynamically creating SQL string where the dynamic part is for data
    is a security disaster waiting to happen (and possible poor
    performance as well). People may think that their upper layers
    will filter the data when they write the code, but eventually
    someone will mess that part up and bang - the database is vulnerable.

    H2 is actually the database that makes it easiest to catch
    that. Put ;ALLOW_LITERALS=NONE in the connection string
    and all attempts to use data values directly instead
    of parameters will fail.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 18:20:50 2025
    On 7/8/2025 5:56 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 09:35:16 -0400, Arne Vajhøj wrote:
    On 7/8/2025 4:14 AM, Louis Krupp wrote:
    As for embedded SQL being nonstandard, I think you might be interested
    in this page:

    https://www.iso.org/standard/84805.html

    which contains this line:

    ISO/IEC 9075-2 specifies embedded SQL for the programming languages: >>> Ada, C, COBOL, Fortran, MUMPS, Pascal, and PL/I.

    Note that while -2 covers native languages, then -10 cover Java.

    I am surprised that they still maintain embedded SQL for Java.
    SQLJ has been out of fashion for like 25 years.

    Yes, but “out of fashion” or not, it’s still supposed to be an official standard according to ISO 9057, isn’t it? Otherwise we could apply the “out of fashion” argument to some or all of those other parts of the spec as well.

    Standards evolve. They add lots of new stuff. And sometimes they remove
    stuff that is not needed anymore.

    Embedded SQL in native languages is still needed.

    Embedded SQL in Java (SQLJ) is not needed anymore so they could have
    dropped it (make it deprecated in N+1 and then remove it in N+2
    or similar).

    Fortran has done a bunch of those:
    https://en.wikipedia.org/wiki/Fortran#Obsolescence_and_deletions

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 23:37:13 2025
    On Tue, 8 Jul 2025 18:20:50 -0400, Arne Vajhøj wrote:

    Standards evolve. They add lots of new stuff. And sometimes they remove
    stuff that is not needed anymore.

    But those standards in particular have not evolved.

    Embedded SQL in native languages is still needed.

    It is still just as clunky an idea as it was when first proposed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 23:38:58 2025
    On Tue, 8 Jul 2025 18:40:31 -0400, Arne Vajhøj wrote:

    Dynamically creating SQL string where the dynamic part is for data is a security disaster waiting to happen (and possible poor performance as
    well).

    That’s a pretty naïve statement to make.

    Quoting literal data in standard SQL is quite simple: turn the data into a string literal with single quotation marks, and any embedded single
    quotation marks are written twice. That’s it. Every other character can be represented as itself, literally.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 8 23:40:52 2025
    On Tue, 8 Jul 2025 18:14:32 -0400, Arne Vajhøj wrote:

    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:

    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:

    $ link main2 + sys$input/opt main1shr/share $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified
    directly on the command line, without having to go through options
    files. Why does the VMS linker need options files for this common case?

    /SHARE in command line means producing a shareable image.

    /SHARE in option file means linking with a shareable image.

    Different meaning based on context.

    Given that the VMS DCL syntax was specifically designed to allow a
    distinction between per-file qualifiers and global qualifiers, this seems
    like a strange limitation, don’t you think?

    Again, *nix linkers are able to manage both on the command line, without needing separate options files.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 20:31:37 2025
    On 7/8/2025 7:37 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 18:20:50 -0400, Arne Vajhøj wrote:
    Standards evolve. They add lots of new stuff. And sometimes they remove
    stuff that is not needed anymore.

    But those standards in particular have not evolved.

    SQL standard has evolved. Lot of stuff has been added.

    1987/1989 -> 1992 -> 1999 -> 2003/2006 -> 2008 -> 2011 -> 2016 -> 2019
    2023

    The SQL standard is organized in parts.

    Part 2 is native languages embedded SQL.

    Part 3 is native languages call API.

    Part 10 is Java embedded SQL.

    Part 13 is SP/UDF in Java.

    They could have deprecated part 10 in maybe 2011 and removed
    it in maybe 2019.

    But they did not.

    Embedded SQL in native languages is still needed.

    It is still just as clunky an idea as it was when first proposed.

    For traditional procedural native languages it reduces code
    size for database code with 10-50% compared to call API and
    I would say that readability is even more improved, because
    the lines saved (typical binding calls) are sometimes very
    complex to handle all sorts of cases.

    So embedded SQL makes perfect sense in that context.

    Modern high level languages usually require a lot less plumbing code,
    so they don't need embedded SQL.

    And an OO language with ORM is requiring way less code than both
    call API and embedded SQL.

    But as long as there is a need for traditional procedural native
    languages to do database access, then embedded SQL makes sense.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 21:54:20 2025
    On 7/8/2025 7:38 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 18:40:31 -0400, Arne Vajhøj wrote:
    Dynamically creating SQL string where the dynamic part is for data is a
    security disaster waiting to happen (and possible poor performance as
    well).

    That’s a pretty naïve statement to make.

    Quoting literal data in standard SQL is quite simple: turn the data into a string literal with single quotation marks, and any embedded single
    quotation marks are written twice. That’s it. Every other character can be represented as itself, literally.

    It is an assumption that all developers remember to do it right. Problem
    is that developers are humans - they make mistakes. Not every time. If
    they are okay then not even often. But a few times. If there are 1000
    places in the code where escape should be used, then there is a pretty
    good chance that it will be forgotten at least 1 time.

    There is a reason why OWASP when it comes to preventing SQL injection
    states:

    <quote>
    Defense Option 4: STRONGLY DISCOURAGED: Escaping All User-Supplied Input </quote>

    Furthermore the escape problem is a little more tricky than
    what you describe.

    Very few API's does not allow prepare/parameters, but a few
    wellknown examples do exist: the recently discussed DBLIB (which
    is one of the reasons why it was replaced by CTLIB in 1993 - 32
    years ago) and the old PHP mysql extension (replaced by mysqli
    extension in 2004 and PDO in 2005).

    Because mysql extension did not support prepare/parameters
    they first added a mysql_escape_string function to do what one
    think should be done.

    $s = mysql_escape_string($s);

    But clever people found out that the argument list was
    wrong.

    It should have connection reference to correctly handle
    escape with various more exotic character set. So
    mysql_real_escape_string function was invented.

    $s = mysql_real_escape_string($s, $con);

    For those that still have PHP 5.x and a MySQL old enough
    to work with PHP 5.x then try:

    <?php
    error_reporting(E_ERROR);

    $con = mysql_connect('192.168.0.10', 'root', '');
    mysql_select_db('test', $con);

    // nothing - all good
    $s = 'BB';
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE f2 = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'nothing - all good: ' . $row['n'] . "\r\n";

    // nothing - injection attempt
    $s = "BB' OR '1'='1";
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE f2 = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'nothing - injection attempt: ' . $row['n'] . "\r\n";

    // bad escape - all good
    $s = 'BB';
    $s = mysql_escape_string($s);
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE f2 = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'escape - all good: ' . $row['n'] . "\r\n";

    // bad escape - injection attempt
    $s = "BB' OR '1'='1";
    $s = mysql_escape_string($s);
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE f2 = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'bad escape - injection attempt: ' . $row['n'] . "\r\n";

    // bad escape - serious injection attempt
    mysql_set_charset('GBK', $con);
    $s = "BB\xbf\x27 OR 1=1 #";
    $s = mysql_escape_string($s);
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE CONVERT(f2 USING
    GBK) = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'bad escape - serious injection attempt: ' . $row['n'] . "\r\n";

    // correct escape - serious injection attempt
    mysql_set_charset('GBK', $con);
    $s = "BB\xbf\x27 OR 1=1 #";
    $s = mysql_real_escape_string($s, $con);
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE CONVERT(f2 USING
    GBK) = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'correct escape - serious injection attempt: ' . $row['n'] . "\r\n";

    // fucked up correct escape - serious injection attempt mysql_set_charset('latin1', $con); // we have to reset
    mysql_query('SET NAMES GBK');
    $s = "BB\xbf\x27 OR 1=1 #";
    $s = mysql_real_escape_string($s, $con);
    $rs = mysql_query("SELECT COUNT(*) AS n FROM t1 WHERE CONVERT(f2 USING
    GBK) = '$s'", $con);
    $row = mysql_fetch_array($rs, MYSQL_ASSOC);
    echo 'fucked up correct escape - serious injection attempt: ' .
    $row['n'] . "\r\n";

    mysql_close($con);


    nothing - all good: 1
    nothing - injection attempt: 3
    escape - all good: 1
    bad escape - injection attempt: 0
    bad escape - serious injection attempt: 3
    correct escape - serious injection attempt: 0
    fucked up correct escape - serious injection attempt: 3

    If you escape manually then you will live to regret it.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 8 22:18:27 2025
    On 7/8/2025 7:40 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 18:14:32 -0400, Arne Vajhøj wrote:
    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:
    Note that, on *nix systems, shareable libraries can be specified
    directly on the command line, without having to go through options
    files. Why does the VMS linker need options files for this common case?

    /SHARE in command line means producing a shareable image.

    /SHARE in option file means linking with a shareable image.

    Different meaning based on context.

    Given that the VMS DCL syntax was specifically designed to allow a distinction between per-file qualifiers and global qualifiers, this seems like a strange limitation, don’t you think?

    Not really.

    First I don't think a verb qualifier and a per-file qualifier with same
    name will work well.

    Second even if it did work, then I think it would be confusing
    to have the qualifier in a command line with two different
    meanings.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 9 07:25:57 2025
    On Tue, 8 Jul 2025 21:54:20 -0400, Arne Vajhøj wrote:

    On 7/8/2025 7:38 PM, Lawrence D'Oliveiro wrote:

    On Tue, 8 Jul 2025 18:40:31 -0400, Arne Vajhøj wrote:

    Dynamically creating SQL string where the dynamic part is for data
    is a security disaster waiting to happen (and possible poor
    performance as well).

    That’s a pretty naïve statement to make.

    Quoting literal data in standard SQL is quite simple: turn the data
    into a string literal with single quotation marks, and any embedded
    single quotation marks are written twice. That’s it. Every other
    character can be represented as itself, literally.

    It is an assumption that all developers remember to do it right.

    It’s not just literal strings. Other constructs need escaping, too.

    <quote>
    Defense Option 4: STRONGLY DISCOURAGED: Escaping All User-Supplied Input </quote>

    Unfortunately, you often have no choice.

    Very few API's does not allow prepare/parameters ...

    None of them include support for all the necessary cases.

    Because mysql extension did not support prepare/parameters
    they first added a mysql_escape_string function to do what one
    think should be done.

    $s = mysql_escape_string($s);

    But clever people found out that the argument list was
    wrong.

    That was just the usual PHP brain damage. Others were able to do it
    right from the beginning.

    error_reporting(E_ERROR);

    Here’s another example of PHP brain damage: the fact that reporting
    SQL errors is *optional*!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 9 07:27:29 2025
    On Tue, 8 Jul 2025 22:18:27 -0400, Arne Vajhøj wrote:

    On 7/8/2025 7:40 PM, Lawrence D'Oliveiro wrote:

    On Tue, 8 Jul 2025 18:14:32 -0400, Arne Vajhøj wrote:

    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:

    Note that, on *nix systems, shareable libraries can be specified
    directly on the command line, without having to go through
    options files. Why does the VMS linker need options files for
    this common case?

    /SHARE in command line means producing a shareable image.

    /SHARE in option file means linking with a shareable image.

    Different meaning based on context.

    Given that the VMS DCL syntax was specifically designed to allow a
    distinction between per-file qualifiers and global qualifiers, this
    seems like a strange limitation, don’t you think?

    Not really.

    First I don't think a verb qualifier and a per-file qualifier with
    same name will work well.

    Second even if it did work, then I think it would be confusing to
    have the qualifier in a command line with two different meanings.

    You make it sound like allowing different contexts for qualifiers is a
    bug, not a feature.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 9 07:22:19 2025
    On Tue, 8 Jul 2025 20:31:37 -0400, Arne Vajhøj wrote:

    On 7/8/2025 7:37 PM, Lawrence D'Oliveiro wrote:

    On Tue, 8 Jul 2025 18:20:50 -0400, Arne Vajhøj wrote:

    Standards evolve. They add lots of new stuff. And sometimes they
    remove stuff that is not needed anymore.

    But those standards in particular have not evolved.

    SQL standard has evolved. Lot of stuff has been added.

    But nothing new in EXEC SQL. For example, did they offer any
    equivalent to the following utility functions?

    def sql_string_list(the_list) :
    "returns a list containing the quoted items of the_list, suitable" \
    " for use in an “in” clause."
    return \
    "(" + ", ".join([sql_string(s) for s in the_list]) + ")"
    #end sql_string_list

    def escape_sql_wild(s, escch) :
    "escapes SQL pattern wildcards in s with escch. The same escch needs" \
    " to be passed to the ESCAPE clause for the LIKE operator."
    if not isinstance(s, str) :
    raise TypeError("expecting s to be a string")
    #end if
    if not isinstance(escch, str) or len(escch) != 1 :
    raise TypeError("expecting escch to be a single-character string")
    #end if
    result = []
    for ch in s :
    if ch == escch or ch == "%" or ch == "_" :
    result.append(escch)
    #end if
    result.append(ch)
    #end for
    return "".join(result)
    #end escape_sql_wild

    def escape_sql_name(n, escch = "\"") :
    "converts n to escaped form to avoid potential conflicts with SQL keywords" \
    " and other syntax errors (e.g. from embedded spaces). escch should be “\"”" \
    " as per the SQL standard."
    assert len(escch) == 1
    out = []
    for i, split1 in enumerate(n.split(escch)) :
    if i != 0 :
    out.append(escch * 2)
    #end if
    out.append(split1)
    #end for
    return escch + "".join(out) + escch
    #end escape_sql_name

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From hb0815@21:1/5 to All on Wed Jul 9 12:16:53 2025
    On 7/9/25 04:18, Arne Vajhøj wrote:
    First I don't think a verb qualifier and a per-file qualifier with same
    name will work well.

    Depends on what you mean with "well". It works and it is used: see
    $ HELP SEARCH /KEY
    ... and documented in the Utility Routines Manual.

    If you do not want to use an option, you can (create and) add your
    shareable image to a shareable image library. Such a library can be
    used on the command line - with the /LIBRARY qualifier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 9 10:13:18 2025
    On 7/9/2025 6:16 AM, hb0815 wrote:
    On 7/9/25 04:18, Arne Vajhøj wrote:
    First I don't think a verb qualifier and a per-file qualifier with same
    name will work well.

    Depends on what you mean with "well". It works and it is used: see
    $ HELP SEARCH /KEY
    ... and documented in the Utility Routines Manual.

    The issue is that I cannot find a path prefix that restrict qualifier
    to the parameter for usage by CLI$PRESENT.

    $ type duo.cld
    define verb duo
    image "sys$disk:[]duo"
    parameter p1, value(type=$file, list, required)
    qualifier q, placement=positional
    $ type duo.pas
    [inherit('sys$library:pascal$cli_routines')]
    program duo(input,output);

    type
    pstr = varying [255] of char;

    var
    fnm, a, b : pstr;

    begin
    if odd(cli$present('Q')) then begin
    writeln('Q=present')
    end else begin
    writeln('Q=notpresent')
    end;
    while odd(cli$get_value('P1', fnm.body, fnm.length)) do begin
    write(fnm);
    if odd(cli$present('Q')) then begin
    write(' Q=present')
    end else begin
    write(' Q=notpresent')
    end;
    writeln;
    end;
    end.
    $ set command duo
    $ pas duo
    $ link duo
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=present
    3.txt Q=present

    Where:
    2.txt Q=present
    is not what was intended.

    In general I like CLD and CLI$ concept, but I would have preferred
    CLI$ to expose the context they use instead of having it hidden.

    If you do not want to use an option, you can (create and) add your
    shareable image to a shareable image library. Such a library can be
    used on the command line - with the /LIBRARY qualifier.

    Yes.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From hb0815@21:1/5 to All on Wed Jul 9 18:51:32 2025
    On 7/9/25 16:13, Arne Vajhøj wrote:
    ...
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=present
    3.txt Q=present

    Where:
      2.txt Q=present
    is not what was intended.

    If you look at the return code and not only check for success, you get
    the information that a qualifier is global or local With a small C
    program, I get

    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=global
    1.txt:Q=local
    2.txt:Q=global
    3.txt:Q=local
    $

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 9 13:39:41 2025
    On 7/9/2025 12:51 PM, hb0815 wrote:
    On 7/9/25 16:13, Arne Vajhøj wrote:
    ...
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=present
    3.txt Q=present

    Where:
       2.txt Q=present
    is not what was intended.

    If you look at the return code and not only check for success, you get
    the information that a qualifier is global or local With a small C
    program, I get

    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=global
    1.txt:Q=local
    2.txt:Q=global
    3.txt:Q=local

    Ah. There is a way. Not using path but via return status.

    $ type duo.cld
    define verb duo
    image "sys$disk:[]duo"
    parameter p1, value(type=$file, list, required)
    qualifier q, placement=positional
    $ type duo.pas [inherit('sys$library:pascal$cli_routines','sys$library:starlet')]
    program duo(input,output);

    type
    pstr = varying [255] of char;

    var
    fnm, a, b : pstr;

    begin
    if cli$present('Q') = CLI$_PRESENT then begin
    writeln('Q=present')
    end else begin
    writeln('Q=notpresent')
    end;
    while odd(cli$get_value('P1', fnm.body, fnm.length)) do begin
    write(fnm);
    if cli$present('Q') = CLI$_LOCPRES then begin
    write(' Q=present')
    end else begin
    write(' Q=notpresent')
    end;
    writeln;
    end;
    end.
    $ set command duo
    $ pas duo
    $ link duo
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present

    I am still not keen on the approach of the same
    qualifier having different meaning for verb and
    parameters though.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Townley@21:1/5 to All on Wed Jul 9 18:56:34 2025
    On 09/07/2025 18:39, Arne Vajhøj wrote:
    On 7/9/2025 12:51 PM, hb0815 wrote:
    On 7/9/25 16:13, Arne Vajhøj wrote:
    ...
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=present
    3.txt Q=present

    Where:
       2.txt Q=present
    is not what was intended.

    If you look at the return code and not only check for success, you get
    the information that a qualifier is global or local With a small C
    program, I get

    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=global
    1.txt:Q=local
    2.txt:Q=global
    3.txt:Q=local

    Ah. There is a way. Not using path but via return status.

    $ type duo.cld
    define verb duo
        image "sys$disk:[]duo"
        parameter p1, value(type=$file, list, required)
    qualifier q, placement=positional
    $ type duo.pas [inherit('sys$library:pascal$cli_routines','sys$library:starlet')]
    program duo(input,output);

    type
       pstr = varying [255] of char;

    var
       fnm, a, b : pstr;

    begin
       if cli$present('Q') = CLI$_PRESENT then begin
          writeln('Q=present')
       end else begin
          writeln('Q=notpresent')
       end;
       while odd(cli$get_value('P1', fnm.body, fnm.length)) do begin
          write(fnm);
          if cli$present('Q') = CLI$_LOCPRES then begin
             write(' Q=present')
          end else begin
             write(' Q=notpresent')
          end;
          writeln;
       end;
    end.
    $ set command duo
    $ pas duo
    $ link duo
    $ duo 1.txt/q,2.txt,3.txt/q
    Q=notpresent
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present
    $ duo/q 1.txt/q,2.txt,3.txt/q
    Q=present
    1.txt Q=present
    2.txt Q=notpresent
    3.txt Q=present

    I am still not keen on the approach of the same
    qualifier having different meaning for verb and
    parameters though.

    Arne


    I cannot think of any examples at the moment, but ISTR a few times this
    happens in DCL

    --
    Chris

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From hb0815@21:1/5 to All on Wed Jul 9 21:55:22 2025
    On 7/9/25 19:39, Arne Vajhøj wrote:
    ...
    I am still not keen on the approach of the same
    qualifier having different meaning for verb and
    parameters though.

    In general, not just for the linker? I intentionally didn't comment on
    that. I only wanted to point out that it works. I'm not in a position to
    decide whether or not to use this CLI feature in the linker. The
    existing behaviour needs to be preserved and a few builds may break. So
    this may not be worth the effort. However, anyone can submit a System Improvement Request (SIR).

    For linking with a single shareable, there is also a one-liner like:
    $ pipe write sys$output "s/share" | link m,sys$pipe/opt

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 9 15:33:50 2025
    On 7/9/2025 3:25 AM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 21:54:20 -0400, Arne Vajhøj wrote:
    On 7/8/2025 7:38 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 18:40:31 -0400, Arne Vajhøj wrote:
    Dynamically creating SQL string where the dynamic part is for data
    is a security disaster waiting to happen (and possible poor
    performance as well).

    That’s a pretty naïve statement to make.

    Quoting literal data in standard SQL is quite simple: turn the data
    into a string literal with single quotation marks, and any embedded
    single quotation marks are written twice. That’s it. Every other
    character can be represented as itself, literally.

    It is an assumption that all developers remember to do it right.

    <quote>
    Defense Option 4: STRONGLY DISCOURAGED: Escaping All User-Supplied Input
    </quote>

    Unfortunately, you often have no choice.

    You practically always have a choice.

    Very few API's does not allow prepare/parameters ...

    None of them include support for all the necessary cases.

    People seems to be able to make it do.

    Because mysql extension did not support prepare/parameters
    they first added a mysql_escape_string function to do what one
    think should be done.

    $s = mysql_escape_string($s);

    But clever people found out that the argument list was
    wrong.

    That was just the usual PHP brain damage. Others were able to do it
    right from the beginning.

    Your escape function does not have database connection
    either.

    :-)

    error_reporting(E_ERROR);

    Here’s another example of PHP brain damage: the fact that reporting
    SQL errors is *optional*!

    ????

    Reporting of SQL errors is not optional in PHP.

    It either give an error code or an exception depending on config.

    error_reporting(E_ERROR) is not to enable errors but to disable
    warnings. I have a PHP old enough to still have mysql extension,
    but I do not have a PHP old enough not to give warnings about
    use of mysql extension.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 9 23:07:22 2025
    On Wed, 9 Jul 2025 15:33:50 -0400, Arne Vajhøj wrote:

    On 7/9/2025 3:25 AM, Lawrence D'Oliveiro wrote:

    On Tue, 8 Jul 2025 21:54:20 -0400, Arne Vajhøj wrote:

    <quote>
    Defense Option 4: STRONGLY DISCOURAGED: Escaping All User-Supplied
    Input </quote>

    Unfortunately, you often have no choice.

    You practically always have a choice.

    Fine. Show alternatives to the code I posted that offers this “choice” you speak of.

    Your escape function does not have database connection either.

    Which one? Naturally escaping/quoting is a common need every time you
    encounter the situation of embedding one language inside another.

    Reporting of SQL errors is not optional in PHP.

    If it wasn’t “optional”, why would you need to enable it via the “error_reporting()” function?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 9 19:51:30 2025
    On 7/9/2025 7:07 PM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 15:33:50 -0400, Arne Vajhøj wrote:
    Reporting of SQL errors is not optional in PHP.

    If it wasn’t “optional”, why would you need to enable it via the “error_reporting()” function?

    I wrote:

    # error_reporting(E_ERROR) is not to enable errors but to disable
    # warnings. I have a PHP old enough to still have mysql extension,
    # but I do not have a PHP old enough not to give warnings about
    # use of mysql extension.

    Is that unclear?

    error_reporting(E_ERROR) is not need to get SQL errors - it
    is to avoid getting warnings about mysql extension being obsolete.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 9 20:04:34 2025
    On 7/9/2025 3:22 AM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 20:31:37 -0400, Arne Vajhøj wrote:
    On 7/8/2025 7:37 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 18:20:50 -0400, Arne Vajhøj wrote:
    Standards evolve. They add lots of new stuff. And sometimes they
    remove stuff that is not needed anymore.

    But those standards in particular have not evolved.

    SQL standard has evolved. Lot of stuff has been added.

    But nothing new in EXEC SQL. For example, did they offer any
    equivalent to the following utility functions?

    def sql_string_list(the_list) :
    "returns a list containing the quoted items of the_list, suitable" \
    " for use in an “in” clause."
    return \
    "(" + ", ".join([sql_string(s) for s in the_list]) + ")"
    #end sql_string_list

    def escape_sql_wild(s, escch) :
    "escapes SQL pattern wildcards in s with escch. The same escch needs" \
    " to be passed to the ESCAPE clause for the LIKE operator."
    if not isinstance(s, str) :
    raise TypeError("expecting s to be a string")
    #end if
    if not isinstance(escch, str) or len(escch) != 1 :
    raise TypeError("expecting escch to be a single-character string")
    #end if
    result = []
    for ch in s :
    if ch == escch or ch == "%" or ch == "_" :
    result.append(escch)
    #end if
    result.append(ch)
    #end for
    return "".join(result)
    #end escape_sql_wild

    def escape_sql_name(n, escch = "\"") :
    "converts n to escaped form to avoid potential conflicts with SQL keywords" \
    " and other syntax errors (e.g. from embedded spaces). escch should be “\"”" \
    " as per the SQL standard."
    assert len(escch) == 1
    out = []
    for i, split1 in enumerate(n.split(escch)) :
    if i != 0 :
    out.append(escch * 2)
    #end if
    out.append(split1)
    #end for
    return escch + "".join(out) + escch
    #end escape_sql_name

    IN is tricky due to the variable number of data.

    But dynamic SQL with dynamic data is still bad.

    I would write your code a little simpler:

    $ type dyn.py
    import sqlite3

    def esc(s):
    return "''".join(s.split("'"))

    def q(s):
    return f"'{s}'"

    def dump_range(con, vals):
    jvals = ",".join(q(esc(val)) for val in vals)
    c = con.cursor()
    c.execute(f"SELECT f1,f2 FROM t1 WHERE f2 IN ({jvals})")
    for row in c.fetchall():
    print('%d %s' % (row[0], row[1]))

    with sqlite3.connect('test.db') as con:
    dump_range(con, ['A', 'CCC', 'EEEEE'])
    dump_range(con, ['BB', 'DDDD'])
    $ python dyn.py
    1 A
    3 CCC
    5 EEEEE
    2 BB
    4 DDDD

    But it is both simpler and safer to use dynamic SQL
    with just dynamic non-data:

    $ type sta.py
    import sqlite3

    def dump_range(con, vals):
    jparm = ",".join('?' for val in vals)
    c = con.cursor()
    c.execute(f"SELECT f1,f2 FROM t1 WHERE f2 IN ({jparm})", vals)
    for row in c.fetchall():
    print('%d %s' % (row[0], row[1]))

    with sqlite3.connect('test.db') as con:
    dump_range(con, ['A', 'CCC', 'EEEEE'])
    dump_range(con, ['BB', 'DDDD'])
    $ python sta.py
    1 A
    3 CCC
    5 EEEEE
    2 BB
    4 DDDD

    It is even more tricky in embedded SQL, but of course
    it is possible.

    You can do the same solution with dynamic SQL with just
    dynamic non-data by using EXEC SQL with PREPARE and EXECUTE
    (that requires you to use SQLDA).

    Possible but requires some code.

    Easier to use a LOCAL TEMPORARY TABLE and stay static.

    Cobol Rdb example:

    $ type emb.sco
    identification division.
    program-id. emb.
    *
    data division.
    working-storage section.
    EXEC SQL BEGIN DECLARE SECTION END-EXEC
    EXEC SQL INCLUDE SQLCA END-EXEC.
    01 con pic x(255).
    01 f1 pic 9(9) display.
    01 f2 pic x(50).
    01 inclause.
    03 nvals pic 9(9).
    03 vals pic x(50) occurs 100 times.
    01 i pic 9(9) comp.
    EXEC SQL END DECLARE SECTION END-EXEC
    EXEC SQL DECLARE curs CURSOR FOR SELECT f1,f2 FROM t1 WHERE f2 IN
    (SELECT f2 FROM f2list) END-EXEC.

    procedure division.
    main-paragraph.
    move "FILENAME disk4:[rdb]test" to con
    EXEC SQL CONNECT TO :con END-EXEC
    move 2 to nvals
    move "BB" to vals(1)
    move "CCC" to vals(2)
    perform dump-range-paragraph
    move 2 to nvals
    move "A" to vals(1)
    move "BB" to vals(2)
    perform dump-range-paragraph
    stop run.
    dump-range-paragraph.
    perform varying i from 1 by 1 until i > nvals
    move vals(i) to f2
    EXEC SQL INSERT INTO f2list VALUES(:f2) END-EXEC
    end-perform
    EXEC SQL OPEN curs END-EXEC
    move 0 to SQLCODE
    perform until not SQLCODE = 0
    EXEC SQL FETCH curs INTO :f1, :f2 END-EXEC
    if SQLCODE = 0
    display f1 " " f2
    end-if
    end-perform
    EXEC SQL CLOSE curs END-EXEC
    EXEC SQL DELETE FROM f2list END-EXEC.
    $ sqlpre /cob /sqloptions=connect emb
    $ link emb + sys$library:sql$user73/libr
    $ run emb
    000000002 BB
    000000003 CCC
    000000001 A
    000000002 BB

    It could probably be done better by a real Cobol
    programmer, but ...

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 9 20:25:06 2025
    On 7/9/2025 8:04 PM, Arne Vajhøj wrote:
    I would write your code a little simpler:

    $ type dyn.py
    import sqlite3

    def esc(s):
        return "''".join(s.split("'"))

    def q(s):
        return f"'{s}'"

    def dump_range(con, vals):
        jvals = ",".join(q(esc(val)) for val in vals)
        c = con.cursor()
        c.execute(f"SELECT f1,f2 FROM t1 WHERE f2 IN ({jvals})")
        for row in c.fetchall():
            print('%d %s' % (row[0], row[1]))

    with sqlite3.connect('test.db') as con:
        dump_range(con, ['A', 'CCC', 'EEEEE'])
        dump_range(con, ['BB', 'DDDD'])
    $ python dyn.py
    1 A
    3 CCC
    5 EEEEE
    2 BB
    4 DDDD

    But it is both simpler and safer to use dynamic SQL
    with just dynamic non-data:

    $ type sta.py
    import sqlite3

    def dump_range(con, vals):
        jparm = ",".join('?' for val in vals)
        c = con.cursor()
        c.execute(f"SELECT f1,f2 FROM t1 WHERE f2 IN ({jparm})", vals)
        for row in c.fetchall():
            print('%d %s' % (row[0], row[1]))

    with sqlite3.connect('test.db') as con:
        dump_range(con, ['A', 'CCC', 'EEEEE'])
        dump_range(con, ['BB', 'DDDD'])
    $ python sta.py
    1 A
    3 CCC
    5 EEEEE
    2 BB
    4 DDDD

    But this is fun:

    $ define/nolog jython_libs "/javalib/zxjdbc.jar:/javalib/h2-2_2_220.jar"
    $ type dyn2.py
    from com.ziclix.python.sql import zxJDBC

    def esc(s):
    return "''".join(s.split("'"))

    def q(s):
    return "'" + s + "'"

    def dump_range(con, vals):
    jvals = ",".join(q(esc(val)) for val in vals)
    c = con.cursor()
    c.execute("SELECT f1,f2 FROM t1 WHERE f2 IN (" + jvals + ")")
    for row in c.fetchall():
    print('%d %s' % (row[0], row[1]))

    with zxJDBC.connect('jdbc:h2:./test;FILE_LOCK=FS;ALLOW_LITERALS=NONE',
    'sa', 'hemmeligt', 'org.h2.Driver') as con:
    dump_range(con, ['A', 'CCC', 'EEEEE'])
    dump_range(con, ['BB', 'DDDD'])
    $ jython dyn2.py
    Traceback (most recent call last):
    File "DYN2.PY", line 17, in <module>
    dump_range(con, ['A', 'CCC', 'EEEEE'])
    File "DYN2.PY", line 12, in dump_range
    c.execute("SELECT f1,f2 FROM t1 WHERE f2 IN (" + jvals + ")") zxJDBC.Error: Literals of this kind are not allowed; SQL statement:
    SELECT f1,f2 FROM t1 WHERE f2 IN ('A','CCC','EEEEE') [90116-220]
    [SQLCode: 90116], [SQLState: 90116]
    $ type sta2.py
    from com.ziclix.python.sql import zxJDBC

    def dump_range(con, vals):
    jparm = ",".join('?' for val in vals)
    c = con.cursor()
    c.execute("SELECT f1,f2 FROM t1 WHERE f2 IN (" + jparm +")", vals)
    for row in c.fetchall():
    print('%d %s' % (row[0], row[1]))

    with zxJDBC.connect('jdbc:h2:./test;FILE_LOCK=FS;ALLOW_LITERALS=NONE',
    'sa', 'hemmeligt', 'org.h2.Driver') as con:
    dump_range(con, ['A', 'CCC', 'EEEEE'])
    dump_range(con, ['BB', 'DDDD'])
    $ jython sta2.py
    1 A
    3 CCC
    5 EEEEE
    2 BB
    4 DDDD

    :-)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to you want to on Thu Jul 10 01:33:08 2025
    On Wed, 9 Jul 2025 20:04:34 -0400, Arne Vajhøj wrote:

    I would write your code a little simpler:

    I notice you didn’t try to offer alternatives to the wildcard and
    identifier escaping.

    def dump_range(con, vals):
    jvals = ",".join(q(esc(val)) for val in vals)
    c = con.cursor()
    c.execute(f"SELECT f1,f2 FROM t1 WHERE f2 IN ({jvals})")
    for row in c.fetchall():
    print('%d %s' % (row[0], row[1]))

    Instead of using my sql_string_list() function everywhere it’s needed,
    you want to write out the full expression at every point?

    But dynamic SQL with dynamic data is still bad.

    Is it bad to offer users dynamic query capabilities?

    Here’s some code I wrote back in Python-2 days (MySQL-specific):

    # collect list of items matching specified search criteria
    condition = \
    (
    list
    ( # free-text fields
    "%(name)s like %(value)s"
    %
    {
    "name" : field[0],
    "value" :
    SQLString("%" + EscapeSQLWild(Params.getvalue(field[1])) + "%"),
    }
    for field in
    (
    ("make", "search_make"),
    ("model", "search_model"),
    ("details", "search_details"),
    ("serial_nr", "search_serial"),
    ("inventory_nr", "search_invent"),
    )
    if Params.getvalue(field[1]) != ""
    )
    +
    list
    ( # exact-match fields
    "%(name)s = %(value)s"
    %
    {
    "name" : field[0],
    "value" : SQLString(Params.getvalue(field[1])),
    }
    for field in
    (
    ("class_name", "search_class"),
    ("allocation", "search_allocated"),
    ("location_name", "search_location"),
    )
    if Params.getvalue(field[1]) != ""
    )
    +
    list
    ( # date fields
    "("
    +
    " or ".join
    (
    "%(name)s %(op)s %(value)s"
    %
    {
    "name" : field[0],
    "op" : op[0],
    "value" : SQLString(Params.getvalue(field[1])),
    }
    for op in
    (
    ("<", "lt"),
    ("=", "eq"),
    (">", "gt"),
    )
    if GetCheckbox("%(name)s[%(op)s]" % {"name" : field[1], "op" : op[1]})
    )
    +
    ")"
    for field in
    (
    ("when_purchased", "search_when_purchased"),
    ("warranty_expiry", "search_warranty_expiry"),
    )
    if reduce
    (
    operator.__or__,
    (
    GetCheckbox("%(name)s[%(op)s]" % {"name" : field[1], "op" : op})
    for op in ("lt", "eq", "gt")
    )
    )
    )
    )
    condition = " and ".join(condition)

    used as

    select «fields» from «table» where «condition»

    Let’s see your EXEC SQL cope with that ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 01:35:54 2025
    On Wed, 9 Jul 2025 20:25:06 -0400, Arne Vajhøj wrote:

    zxJDBC.Error: Literals of this kind are not allowed; SQL statement:
    SELECT f1,f2 FROM t1 WHERE f2 IN ('A','CCC','EEEEE') [90116-220]
    [SQLCode: 90116], [SQLState: 90116]

    What happens if you really want literals in your SQL code?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 01:21:48 2025
    On Wed, 9 Jul 2025 19:51:30 -0400, Arne Vajhøj wrote:

    error_reporting(E_ERROR) is not need to get SQL errors - it is to avoid getting warnings about mysql extension being obsolete.

    I think you need

    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

    in order to get error exceptions raised about incorrect MySQL. Sensible languages would do this by default.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 9 22:26:19 2025
    On 7/9/2025 9:35 PM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 20:25:06 -0400, Arne Vajhøj wrote:
    zxJDBC.Error: Literals of this kind are not allowed; SQL statement:
    SELECT f1,f2 FROM t1 WHERE f2 IN ('A','CCC','EEEEE') [90116-220]
    [SQLCode: 90116], [SQLState: 90116]

    What happens if you really want literals in your SQL code?

    You don't.

    :-)

    But those that want permission to shoot themselves in
    the foot can omit ;ALLOW_LITERALS=NONE in the connection
    string.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 9 22:24:50 2025
    On 7/9/2025 9:21 PM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 19:51:30 -0400, Arne Vajhøj wrote:

    error_reporting(E_ERROR) is not need to get SQL errors - it is to avoid
    getting warnings about mysql extension being obsolete.

    I think you need

    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

    in order to get error exceptions raised about incorrect MySQL. Sensible languages would do this by default.

    There are fundamentally two different ways of handling errors:
    * return status
    * exception

    Different languages use different approaches. C use return status.
    Java use exception.

    PHP give you the choice of which you want (for mysqli and pdo mysql
    and many other).

    $ type err1.php
    <?php

    $con = mysqli_connect('arnepc5', 'arne', 'hemmeligt', 'test'); mysqli_report(MYSQLI_REPORT_OFF);
    $stmt = mysqli_prepare($con, 'SELECT the data needed');
    if(!$stmt) {
    echo mysqli_error($con) . "\r\n";
    goto closecon;
    }
    // whatever
    closestmt: mysqli_stmt_close($stmt);
    closecon: mysqli_close($con);

    $ php err1.php
    You have an error in your SQL syntax; check the manual that corresponds
    to your MySQL server version for the right syntax to use near 'needed'
    at line 1
    $ type err2.php
    <?php

    $con = mysqli_connect('arnepc5', 'arne', 'hemmeligt', 'test'); mysqli_report(MYSQLI_REPORT_ALL);
    try {
    $stmt = mysqli_prepare($con, 'SELECT the data needed');
    // whatever
    mysqli_stmt_close($stmt);
    mysqli_close($con);
    } catch (mysqli_sql_exception $ex) {
    echo $ex->getMessage() . "\r\n";
    mysqli_close($con);
    }

    $ php err2.php
    You have an error in your SQL syntax; check the manual that corresponds
    to your MySQL server version for the right syntax to use near 'needed'
    at line 1

    Default in recent PHP versions is exceptions.

    I prefer exceptions over return status, but not everybody agrees.

    PHP provide a way to use return status for those that prefer that.

    Some must prefer it that way. There are also newer languages
    being created without exceptions.

    I would say that in general high level languages do exceptions
    while low level languages don't do exceptions.

    PHP is a high level language, but while PHP in recent years
    has been very inspired by Java, then in the beginning it was
    very inspired by C. It got some baggage - a common example
    is all the strxxx functions.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 04:28:13 2025
    On Wed, 9 Jul 2025 22:24:50 -0400, Arne Vajhøj wrote:

    There are fundamentally two different ways of handling errors:
    * return status
    * exception

    There’s a difference between different statuses that might be returned as part of the normal execution of an operation, and unexpected conditions indicating program bugs.

    Syntax errors in the embedded language would almost always belong in the
    latter category.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 05:48:55 2025
    On Wed, 9 Jul 2025 22:26:19 -0400, Arne Vajhøj wrote:

    On 7/9/2025 9:35 PM, Lawrence D'Oliveiro wrote:

    On Wed, 9 Jul 2025 20:25:06 -0400, Arne Vajhøj wrote:

    zxJDBC.Error: Literals of this kind are not allowed; SQL statement:
    SELECT f1,f2 FROM t1 WHERE f2 IN ('A','CCC','EEEEE') [90116-220]
    [SQLCode: 90116], [SQLState: 90116]

    What happens if you really want literals in your SQL code?

    You don't.

    Seems like somebody is really, really scared of the possibilities offered
    by being able to embed one language inside another. Just because PHP programmers can’t get their heads around it, doesn’t mean the rest of us have to be tied down to the same mental level.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to [email protected] on Thu Jul 10 12:00:31 2025
    In article <[email protected]>,
    bill <[email protected]> wrote:
    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:

    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified directly
    on the command line, without having to go through options files. Why does
    the VMS linker need options files for this common case?

    See above... "on *nix systems"
    He doesn't care about VMS. STOP FEEDING HE TROLL!!!!!

    I think that Arne may be incapable of that. He seems unable to
    refrain from engaging, and clearly asking him to stop for the
    benefit of other readers has been ineffective thus far.

    Perhaps the best thing is to just plonk any part of a thread
    where someone responds to the troll, whether the responder is
    Arne or anyone else. I have no reason to believe that asking
    Arne again is going to yield a different result.

    Most USENET newsgroups used to have an FAQ; it may be useful to
    restart that for comp.os.vms (among others), perhaps with a
    section on well-known trolls.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jul 10 15:52:20 2025
    On 7/10/2025 3:50 PM, Arne Vajhøj wrote:
    On 7/9/2025 3:55 PM, hb0815 wrote:
    For linking with a single shareable, there is also a one-liner like:
    $ pipe write sys$output "s/share" | link m,sys$pipe/opt

    Clever.

    But not a style I would recommend.

    When I read it then I wondered why you used SYS$PIPE and
    not SYS$INPUT.

    They can be different but I don't think they are in this case.

    And if someone want to see a case where they are different,
    then this weird DCL does:

    $ type pip.txt
    A
    BB
    CCC
    $ type pip.com
    $ vf='f$verify(0)'
    $ type sys$pipe
    $ type sys$input
    CCC
    BB
    A
    $
    $ if vf then exit f$verify(1)+1
    $ exit 1
    $ pipe type pip.txt | @pip
    A
    BB
    CCC
    CCC
    BB
    A

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jul 10 15:50:32 2025
    On 7/9/2025 3:55 PM, hb0815 wrote:
    For linking with a single shareable, there is also a one-liner like:
    $ pipe write sys$output "s/share" | link m,sys$pipe/opt

    Clever.

    But not a style I would recommend.

    When I read it then I wondered why you used SYS$PIPE and
    not SYS$INPUT.

    They can be different but I don't think they are in this case.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 19:14:13 2025
    On 7/10/2025 1:48 AM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 22:26:19 -0400, Arne Vajhøj wrote:
    On 7/9/2025 9:35 PM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 20:25:06 -0400, Arne Vajhøj wrote:
    zxJDBC.Error: Literals of this kind are not allowed; SQL statement:
    SELECT f1,f2 FROM t1 WHERE f2 IN ('A','CCC','EEEEE') [90116-220]
    [SQLCode: 90116], [SQLState: 90116]

    What happens if you really want literals in your SQL code?

    You don't.

    Seems like somebody is really, really scared of the possibilities offered
    by being able to embed one language inside another.

    Embedded SQL, call API with static SQL, call API with dynamic SQL only
    dynamic non-data and call API with dynamic SQL and dynamic data all
    embed SQL in Cobol/C/Java/PHP/Python/whatever.

    Even ORM's tend to embed a language. Either SQL or something ORM
    specific: EJBQL, HQL, JPQL, JDOQL, JDQL, DQL etc..

    So nobody seems afraid of embedding SQL.

    It is just that some want to embed SQL in a safe way - a guaranteed
    safe way.

    Just because PHP programmers can’t get their heads around it, doesn’t mean the rest of us have to be tied down to the same mental level.

    The style you propose match pretty well how PHP developers did things
    back in the 00's. They learned that it was not good. Learned the hard
    way. SQL injection had a very prominent place on OWASP top 10
    for many years.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 19:05:42 2025
    On 7/10/2025 12:28 AM, Lawrence D'Oliveiro wrote:
    On Wed, 9 Jul 2025 22:24:50 -0400, Arne Vajhøj wrote:
    There are fundamentally two different ways of handling errors:
    * return status
    * exception

    There’s a difference between different statuses that might be returned as part of the normal execution of an operation, and unexpected conditions indicating program bugs.

    Syntax errors in the embedded language would almost always belong in the latter category.

    Yes.

    Java operate with checked and unchecked exceptions based on
    that logic.

    But I don't see any point for the problem at hand.

    If the PHP developer configure the database connection
    to not throw exceptions because the developer want to
    check return status, then that must be respected. It
    does not make any sense of the database extension is
    is coded like "SQL syntax is so serious a bug, so even
    though the developer asked me not to throw exceptions, then
    I will throw one anyway". The code is not prepared to
    handle exceptions, because the developer expect the
    driver to throw one when told not to.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jul 10 19:50:05 2025
    On 7/6/2025 6:07 PM, Arne Vajhøj wrote:
    But there are some incompatibilities between H2 in
    PostgreSQL mode and real PostgreSQL.

    And one of them is that H2 in PostgreSQL mode
    does not support server side cursors.

    And embedded SQL without "FETCH cursorname INTO ..."
    is like a hotdog without a hotdog sausage!

    If one can live with SELECT INTO then everything
    is fine (but nobody can).

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    EXEC SQL INCLUDE SQLCA;

    int main()
    {
    EXEC SQL BEGIN DECLARE SECTION;
    char f2[51];
    long int f1;
    char srv[50], un[50], pw[50];
    EXEC SQL END DECLARE SECTION;
    strcpy(srv, "tcp:postgresql://localhost:5435/test");
    strcpy(un, "sa");
    strcpy(pw, "hemmeligt");
    EXEC SQL CONNECT TO :srv USER :un USING :pw;
    f1 = 2;
    EXEC SQL SELECT f2 INTO :f2 FROM t1 WHERE f1 = :f1;
    printf("%s\n", f2);
    return 0;
    }

    works fine.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 23:54:49 2025
    On Thu, 10 Jul 2025 19:14:13 -0400, Arne Vajhøj wrote:

    On 7/10/2025 1:48 AM, Lawrence D'Oliveiro wrote:

    Seems like somebody is really, really scared of the possibilities
    offered by being able to embed one language inside another.

    Embedded SQL, call API with static SQL, call API with dynamic SQL only dynamic non-data and call API with dynamic SQL and dynamic data all
    embed SQL in Cobol/C/Java/PHP/Python/whatever.

    All just so clunky, as we have already seen.

    Even ORM's tend to embed a language. Either SQL or something ORM
    specific: EJBQL, HQL, JPQL, JDOQL, JDQL, DQL etc..

    ORMs are a waste of time. I’ve seen how they deal with complex queries -- it’s just re-expressing the complexity of SQL in another equally complex form.

    So nobody seems afraid of embedding SQL.

    They are afraid of doing it dynamically, though.

    The style you propose match pretty well how PHP developers did things
    back in the 00's. They learned that it was not good.

    Because ... PHP.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Dan Cross on Thu Jul 10 19:23:43 2025
    On 7/10/2025 8:00 AM, Dan Cross wrote:
    In article <[email protected]>,
    bill <[email protected]> wrote:
    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:
    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified directly >>> on the command line, without having to go through options files. Why does >>> the VMS linker need options files for this common case?

    See above... "on *nix systems"
    He doesn't care about VMS. STOP FEEDING HE TROLL!!!!!

    I think that Arne may be incapable of that. He seems unable to
    refrain from engaging, and clearly asking him to stop for the
    benefit of other readers has been ineffective thus far.

    Perhaps the best thing is to just plonk any part of a thread
    where someone responds to the troll, whether the responder is
    Arne or anyone else. I have no reason to believe that asking
    Arne again is going to yield a different result.

    You are free to plonk all the threads you want to plonk.

    And you are also free to post about about plonking this and that.

    I would recommend that you do not post about it as it is
    useless content for other.

    Except if you run a VMS based newsreader and need help on
    how to plonk. That would of course be a totally relevant topic.
    But I have a strong feeling that is not the case.

    But maybe consider this: when people read these threads
    in 10 or 20 or 30 years, then what do you think people
    will find most useful:
    A) your rant(s) about Lawrence and talk about plonking
    B) this subthread covering topics like: syntax for linking
    shareable images vs syntax for linking with shareable images,
    use of CLI$PRESENT return code to distinguish between
    qualifier on verb vs on parameter, difference between
    SYS$PIPE and SYS$INPUT when using PIPE command
    ?

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jul 10 19:57:32 2025
    On 7/9/2025 10:24 PM, Arne Vajhøj wrote:
    PHP give you the choice of which you want (for mysqli and pdo mysql
    and many other).

    $ type err1.php
    <?php

    $con = mysqli_connect('arnepc5', 'arne', 'hemmeligt', 'test'); mysqli_report(MYSQLI_REPORT_OFF);
    $stmt = mysqli_prepare($con, 'SELECT the data needed');
    if(!$stmt) {
        echo mysqli_error($con) . "\r\n";
        goto closecon;
    }
    // whatever
    closestmt: mysqli_stmt_close($stmt);
    closecon: mysqli_close($con);

    $ php err1.php
    You have an error in your SQL syntax; check the manual that corresponds
    to your MySQL server version for the right syntax to use near 'needed'
    at line 1
    $ type err2.php
    <?php

    $con = mysqli_connect('arnepc5', 'arne', 'hemmeligt', 'test'); mysqli_report(MYSQLI_REPORT_ALL);
    try {
        $stmt = mysqli_prepare($con, 'SELECT the data needed');
        // whatever
        mysqli_stmt_close($stmt);
        mysqli_close($con);
    } catch (mysqli_sql_exception $ex) {
        echo $ex->getMessage() . "\r\n";
        mysqli_close($con);
    }

    $ php err2.php
    You have an error in your SQL syntax; check the manual that corresponds
    to your MySQL server version for the right syntax to use near 'needed'
    at line 1

    Default in recent PHP versions is exceptions.

    If someone want the same using PDO.

    (and yes - everybody should use PDO instead of mysqli!)

    $ type err3.php
    <?php

    $con = new PDO('mysql:host=arnepc5;dbname=test', 'arne', 'hemmeligt'); $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
    $stmt = $con->prepare('SELECT the data needed');
    if(!$stmt) {
    echo $con->errorInfo()[2] . "\r\n";
    goto finish;
    }
    if(!$stmt->execute()) {
    echo $stmt->errorInfo()[2] . "\r\n";
    goto finish;
    }
    // whatever
    finish:

    $ php err3.php
    You have an error in your SQL syntax; check the manual that corresponds
    to your MySQL server version for the right syntax to use ne
    ar 'needed' at line 1
    $ type err4.php
    <?php

    $con = new PDO('mysql:host=arnepc5;dbname=test', 'arne', 'hemmeligt'); $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    try {
    $stmt = $con->prepare('SELECT the data needed');
    $stmt->execute();
    // whatever
    } catch (PDOException $ex) {
    echo $ex->getMessage() . "\r\n";
    }

    $ php err4.php
    SQLSTATE[42000]: Syntax error or access violation: 1064 You have an
    error in your SQL syntax; check the manual that corresponds to
    your MySQL server version for the right syntax to use near 'needed' at
    line 1

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 10 23:58:00 2025
    On Thu, 10 Jul 2025 19:05:42 -0400, Arne Vajhøj wrote:

    If the PHP developer configure the database connection to not throw exceptions because the developer want to check return status, then that
    must be respected.

    That’s a dumb way of doing it. Most of the errors from an SQL call
    indicate program bugs. The only exception I have come across is
    IntegrityError, when used to indicate an attempt to add a record with a duplicate value for a key that must be unique. That can sometimes be
    usefully caught and reported back to the user as an error with the data
    that they are trying to enter.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 20:11:59 2025
    On 7/10/2025 7:58 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 19:05:42 -0400, Arne Vajhøj wrote:
    If the PHP developer configure the database connection to not throw
    exceptions because the developer want to check return status, then that
    must be respected.

    That’s a dumb way of doing it. Most of the errors from an SQL call
    indicate program bugs. The only exception I have come across is IntegrityError, when used to indicate an attempt to add a record with a duplicate value for a key that must be unique. That can sometimes be
    usefully caught and reported back to the user as an error with the data
    that they are trying to enter.

    There are other:
    * dynamic SQL without prepare/parameters and a bad value
    * foreign key constraint violation
    * invalid data value
    * transaction rollback due to timeout or deadlock
    * timeout waiting for connection due to connection pool at max
    * timeout waiting for connection due to connection at max in database
    * no connectivity to server
    * invalid login credentials

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 20:23:08 2025
    On 7/10/2025 8:19 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 20:11:59 -0400, Arne Vajhøj wrote:
    On 7/10/2025 7:58 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 19:05:42 -0400, Arne Vajhøj wrote:
    If the PHP developer configure the database connection to not
    throw exceptions because the developer want to check return
    status, then that must be respected.

    That’s a dumb way of doing it. Most of the errors from an SQL call
    indicate program bugs. The only exception I have come across is
    IntegrityError, when used to indicate an attempt to add a record
    with a duplicate value for a key that must be unique. That can
    sometimes be usefully caught and reported back to the user as an
    error with the data that they are trying to enter.

    There are other:
    * dynamic SQL without prepare/parameters and a bad value
    * foreign key constraint violation
    * invalid data value
    * transaction rollback due to timeout or deadlock
    * timeout waiting for connection due to connection pool at max
    * timeout waiting for connection due to connection at max in database
    * no connectivity to server
    * invalid login credentials

    Again, all of that can be returned via exceptions, which I can
    selectively catch as appropriate if they’re relevant to my program
    logic, or leave them to be reported as program bugs if I’m not
    expecting them.

    Yes.

    And in some languages & database technologies you will always
    get an exception. Example: Java.

    In other languages you will never get an exception because
    the language does not support exceptions. Example: C. They have
    to use return status.

    And in PHP (at least for the most common database extensions)
    the developer have the choice - do they want exceptions or do
    they want to test on the return status. Everybody should be happy.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 00:24:17 2025
    On Thu, 10 Jul 2025 19:57:32 -0400, Arne Vajhøj wrote:

    (and yes - everybody should use PDO instead of mysqli!)

    $con = new PDO('mysql:host=arnepc5;dbname=test', 'arne', 'hemmeligt'); $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
    $stmt = $con->prepare('SELECT the data needed');
    if(!$stmt) {
    echo $con->errorInfo()[2] . "\r\n";
    goto finish;
    }
    if(!$stmt->execute()) {
    echo $stmt->errorInfo()[2] . "\r\n";
    goto finish;
    }

    That is so clunky without exceptions. You’d think normal programmers
    would get fed up of continually writing prepare/execute/fetch
    sequences. But PHP programmers don’t seem to think like normal people.

    Here’s a utility function I wrote for my Python code years ago, and
    use every time I want to retrieve data from an SQL database. This is
    the SQLite version using the APSW wrapper:

    def db_iter(conn, cmd, values = None, mapfn = lambda x : x) :
    "executes cmd on a new cursor from connection conn and yields" \
    " the results in turn."
    for item in conn.cursor().execute(cmd, values) :
    yield mapfn(item)
    #end for
    #end db_iter

    So getting and processing records is as simple as

    for entry in db_iter(conn, "select ..." ...) :
    ... do something with entry ...
    #end for

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 00:19:46 2025
    On Thu, 10 Jul 2025 20:11:59 -0400, Arne Vajhøj wrote:

    On 7/10/2025 7:58 PM, Lawrence D'Oliveiro wrote:

    On Thu, 10 Jul 2025 19:05:42 -0400, Arne Vajhøj wrote:

    If the PHP developer configure the database connection to not
    throw exceptions because the developer want to check return
    status, then that must be respected.

    That’s a dumb way of doing it. Most of the errors from an SQL call
    indicate program bugs. The only exception I have come across is
    IntegrityError, when used to indicate an attempt to add a record
    with a duplicate value for a key that must be unique. That can
    sometimes be usefully caught and reported back to the user as an
    error with the data that they are trying to enter.

    There are other:
    * dynamic SQL without prepare/parameters and a bad value
    * foreign key constraint violation
    * invalid data value
    * transaction rollback due to timeout or deadlock
    * timeout waiting for connection due to connection pool at max
    * timeout waiting for connection due to connection at max in database
    * no connectivity to server
    * invalid login credentials

    Again, all of that can be returned via exceptions, which I can
    selectively catch as appropriate if they’re relevant to my program
    logic, or leave them to be reported as program bugs if I’m not
    expecting them.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 00:29:04 2025
    On Thu, 10 Jul 2025 20:23:08 -0400, Arne Vajhøj wrote:

    And in PHP (at least for the most common database extensions)
    the developer have the choice - do they want exceptions or do
    they want to test on the return status. Everybody should be happy.

    Except the default is not to raise exceptions.

    Python makes it easy to catch exceptions. Not only that, it makes it easy
    to filter the ones you want to catch, by a) subclassing so you can be more specific, and b) if even that is not enough, you can query parameters of
    the exception object and do a “raise” statement without arguments to pass it on if your handler doesn’t want to handle it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 20:38:29 2025
    On 7/10/2025 8:29 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 20:23:08 -0400, Arne Vajhøj wrote:
    And in PHP (at least for the most common database extensions)
    the developer have the choice - do they want exceptions or do
    they want to test on the return status. Everybody should be happy.

    Except the default is not to raise exceptions.

    The default in recent versions (and that includes all supported
    versions) is exceptions.

    Python makes it easy to catch exceptions. Not only that, it makes it easy
    to filter the ones you want to catch, by a) subclassing so you can be more specific, and b) if even that is not enough, you can query parameters of
    the exception object and do a “raise” statement without arguments to pass it on if your handler doesn’t want to handle it.

    The ability to subclass exceptions to catch specific and the ability
    to rethrow exceptions are quite common. Not Python specific at all.

    Java use sub-classing extensively for database. A little simplified:

    |->SQLNonTransientException->non transient exception classses SQLException-|
    |->SQLTransientException->transient exception classses

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 10 20:29:56 2025
    On 7/10/2025 7:54 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 19:14:13 -0400, Arne Vajhøj wrote:
    Even ORM's tend to embed a language. Either SQL or something ORM
    specific: EJBQL, HQL, JPQL, JDOQL, JDQL, DQL etc..

    ORMs are a waste of time. I’ve seen how they deal with complex queries -- it’s just re-expressing the complexity of SQL in another equally complex form.

    You can save a lot of code by using ORM.

    Example (Groovy and JPA):

    jpadata = em.createQuery("SELECT DISTINCT o FROM OrdersJPA AS o JOIN
    FETCH o.orderLines ol", OrdersJPA.class).getResultList()

    Gives you a list of order objects that each has a list of order lines
    objects.

    No magic. But the ORM framework handle sending query, reading data
    and stuffing data into object structure. All the trivial stuff that
    just makes the code take longer to read without providing any value.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 00:33:45 2025
    On Thu, 10 Jul 2025 01:33:08 -0000 (UTC), I wrote:

    Is it bad to offer users dynamic query capabilities?

    Here’s some code I wrote back in Python-2 days (MySQL-specific):

    # collect list of items matching specified search criteria
    condition = \
    (
    list
    ( # free-text fields
    "%(name)s like %(value)s"
    %
    {
    "name" : field[0],
    "value" :
    SQLString("%" + EscapeSQLWild(Params.getvalue(field[1])) + "%"),
    }
    for field in
    (
    ("make", "search_make"),
    ("model", "search_model"),
    ("details", "search_details"),
    ("serial_nr", "search_serial"),
    ("inventory_nr", "search_invent"),
    )
    if Params.getvalue(field[1]) != ""
    )
    +
    list
    ( # exact-match fields
    "%(name)s = %(value)s"
    %
    {
    "name" : field[0],
    "value" : SQLString(Params.getvalue(field[1])),
    }
    for field in
    (
    ("class_name", "search_class"),
    ("allocation", "search_allocated"),
    ("location_name", "search_location"),
    )
    if Params.getvalue(field[1]) != ""
    )
    +
    list
    ( # date fields
    "("
    +
    " or ".join
    (
    "%(name)s %(op)s %(value)s"
    %
    {
    "name" : field[0],
    "op" : op[0],
    "value" : SQLString(Params.getvalue(field[1])),
    }
    for op in
    (
    ("<", "lt"),
    ("=", "eq"),
    (">", "gt"),
    )
    if GetCheckbox("%(name)s[%(op)s]" % {"name" : field[1], "op" : op[1]})
    )
    +
    ")"
    for field in
    (
    ("when_purchased", "search_when_purchased"),
    ("warranty_expiry", "search_warranty_expiry"),
    )
    if reduce
    (
    operator.__or__,
    (
    GetCheckbox("%(name)s[%(op)s]" % {"name" : field[1], "op" : op})
    for op in ("lt", "eq", "gt")
    )
    )
    )
    )
    condition = " and ".join(condition)

    used as

    select «fields» from «table» where «condition»

    Let’s see your EXEC SQL cope with that ...

    ... still waiting ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to [email protected] on Fri Jul 11 03:06:49 2025
    In article <104pi1v$13i9u$[email protected]>,
    Arne Vajhøj <[email protected]> wrote:
    On 7/10/2025 8:00 AM, Dan Cross wrote:
    In article <[email protected]>,
    bill <[email protected]> wrote:
    On 7/8/2025 5:58 PM, Lawrence D'Oliveiro wrote:
    On Tue, 8 Jul 2025 07:14:44 -0400, Arne Vajhøj wrote:
    $ link main2 + sys$input/opt
    main1shr/share
    $

    what bucket would you put main1.exe in?

    Note that, on *nix systems, shareable libraries can be specified directly >>>> on the command line, without having to go through options files. Why does >>>> the VMS linker need options files for this common case?

    See above... "on *nix systems"
    He doesn't care about VMS. STOP FEEDING HE TROLL!!!!!

    I think that Arne may be incapable of that. He seems unable to
    refrain from engaging, and clearly asking him to stop for the
    benefit of other readers has been ineffective thus far.

    Perhaps the best thing is to just plonk any part of a thread
    where someone responds to the troll, whether the responder is
    Arne or anyone else. I have no reason to believe that asking
    Arne again is going to yield a different result.

    You are free to plonk all the threads you want to plonk.

    And you are also free to post about about plonking this and that.

    I would recommend that you do not post about it as it is
    useless content for other.

    I would recommend you do not respond to the troll for similar
    reasons. However, at this point, evidence suggests that you are
    incapable of understanding that.

    Except if you run a VMS based newsreader and need help on
    how to plonk. That would of course be a totally relevant topic.
    But I have a strong feeling that is not the case.

    No need for strong feelings: anyone who can read USENET article
    headers can tell that it is, in fact, not the case. But, while
    that is irrelevant, perhaps it is something that you struggle
    with, which is why you waste time speculating instead of simply
    looking and confirming.

    But maybe consider this: when people read these threads
    in 10 or 20 or 30 years, then what do you think people
    will find most useful:
    A) your rant(s) about Lawrence and talk about plonking
    B) this subthread covering topics like: syntax for linking
    shareable images vs syntax for linking with shareable images,
    use of CLI$PRESENT return code to distinguish between
    qualifier on verb vs on parameter, difference between
    SYS$PIPE and SYS$INPUT when using PIPE command
    ?

    If it were just me, you might have a point. But it's not, and
    at least three others recently have echoed pleas for you to
    stop engaging with him.

    Indeed, I was the one suggesting to Bill that continuing to ask
    you to not engage with someone who clearly does not participate
    in good faith, is an exercise in futility and that he shouldn't
    bother. One wonders why you didn't write this cute little rant
    to Bill, after he once again asked you to stop feeding the
    troll?

    To go with your hypothetical, however, I suspect that in 10, 20
    or 30 years, _if_ someone reviews these conversations, they'll
    seriously wonder why you continued to engage this clown on
    uninformed rants about how VMS should have been implemented on
    top of the Linux kernel, and why you kept going as you both
    endlessly chased each others tails speculating about the
    internal VSI engineering and business challenges that lead to
    the long delay in getting the x86 port out, and why you gave
    serious consideration to his clearly ignorant ramblings about
    software development, embedded SQL, and so on.

    Sadly, whatever useful information is in this subthread is
    simply overwhelmed by the sheer volume of the troll's drivel,
    which is dramatically amplified by your inability to refrain
    from responding to him. I suspect that most resonable future
    readers will give up in frustration before getting to the good
    stuff: an effect almost entirely due to you.

    But since you can't figure out how to read an article's headers,
    maybe you just need some help configuring your news reader, so
    let us know if you need a tutorial on how to use a killfile: I'm
    sure someone around here can help, and it is most certainly on
    topic here.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 02:54:36 2025
    On Thu, 10 Jul 2025 20:38:29 -0400, Arne Vajhøj wrote:

    The ability to subclass exceptions to catch specific and the ability
    to rethrow exceptions are quite common. Not Python specific at all.

    Java use sub-classing extensively for database. A little simplified:

    |->SQLNonTransientException->non transient exception classses SQLException-|
    |->SQLTransientException->transient exception classses


    Python has metaclasses. These make it easy to set up an exception
    hierarchy based on status codes (or other criteria). A subclass
    definition can be as simple as

    class HangupNetworkTrouble(HangupCause) :
    _CODES_ = {2, 3, 38, 42}
    #end HangupNetworkTrouble

    Now if the status code is one of those listed, any instantiation of
    the base class will automatically delegate to this subclass.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From hb0815@21:1/5 to All on Fri Jul 11 13:09:17 2025
    On 7/10/25 21:52, Arne Vajhøj wrote:
    On 7/10/2025 3:50 PM, Arne Vajhøj wrote:
    On 7/9/2025 3:55 PM, hb0815 wrote:
    For linking with a single shareable, there is also a one-liner like:
    $ pipe write sys$output "s/share" | link m,sys$pipe/opt

    Clever.

    But not a style I would recommend.

    I don't know what you would recommend. Using pipe is just another
    example. And, if wanted or needed, here you can use symbol substitution.

    When I read it then I wondered why you used SYS$PIPE and
    not SYS$INPUT.

    There is no reason. It's just out of habit.

    They can be different but I don't think they are in this case.

    And if someone want to see a case where they are different,
    then this weird DCL does:
    ...

    As you probably know, the difference is documented. One example is:

    $ help/noprompt/nopage PIPE Description Pipelines_and_TEEs
    Using_TEEs_and_SYS

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Fri Jul 11 10:16:23 2025
    On 7/11/2025 7:09 AM, hb0815 wrote:
    On 7/10/25 21:52, Arne Vajhøj wrote:
    On 7/10/2025 3:50 PM, Arne Vajhøj wrote:
    They can be different but I don't think they are in this case.

    And if someone want to see a case where they are different,
    then this weird DCL does:
    ...

    As you probably know, the difference is documented. One example is:

    $ help/noprompt/nopage PIPE Description Pipelines_and_TEEs
    Using_TEEs_and_SYS

    The difference is sort of as expected - similar to
    SYS$COMMAND and SYS$INPUT.

    Regarding help then everybody knows one can save
    several minutes reading help/documentation by spending
    hours of trial and error.

    :-) :-) :-)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stephen Hoffman@21:1/5 to Dan Cross on Fri Jul 11 18:13:22 2025
    On 2025-07-10 12:00:31 +0000, Dan Cross said:

    Most USENET newsgroups used to have an FAQ; it may be useful to restart
    that for comp.os.vms (among others), perhaps with a section on
    well-known trolls.

    Having some familiarity with the topic, I can assure you that a FAQ
    will get ignored. But if you want to post your own FAQ, by all means
    have at.

    For your concerns here, you can plonk the posters that are assisting
    with getting unwanted posters and postings past others' existing
    filters.

    And if your particular newsreader has more modern filtering
    capabilities, there can be other options.


    --
    Pure Personal Opinion | HoffmanLabs LLC

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 11 23:35:02 2025
    On Thu, 10 Jul 2025 20:29:56 -0400, Arne Vajhøj wrote:

    You can save a lot of code by using ORM.

    Example (Groovy and JPA):

    jpadata = em.createQuery("SELECT DISTINCT o FROM OrdersJPA AS o JOIN
    FETCH o.orderLines ol", OrdersJPA.class).getResultList()

    Gives you a list of order objects that each has a list of order lines objects.

    I thought the whole point of an ORM was to get away from having to hand- construct SQL queries. And yet you have done exactly that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Fri Jul 11 19:43:59 2025
    On 7/11/2025 7:35 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 20:29:56 -0400, Arne Vajhøj wrote:
    You can save a lot of code by using ORM.

    Example (Groovy and JPA):

    jpadata = em.createQuery("SELECT DISTINCT o FROM OrdersJPA AS o JOIN
    FETCH o.orderLines ol", OrdersJPA.class).getResultList()

    Gives you a list of order objects that each has a list of order lines
    objects.

    I thought the whole point of an ORM was to get away from having to hand- construct SQL queries. And yet you have done exactly that.

    That is not SQL but HQL.

    :-)

    No. ORM's either comes with its own query language or allow
    usage of SQL or have an equivalent fluent API or support more
    than one of those.

    The main point of ORM is to avoid boilerplate code like
    iterating and result sets and stuff them into the object data
    structure.

    Some ORM's operate with a dirty concept and automatically
    save changed objects.

    Some ORM's provide identical syntax for doing things even
    though the underlying databases have different SQL syntax for
    it, which help making the application more portable.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sat Jul 12 00:35:37 2025
    On Fri, 11 Jul 2025 19:43:59 -0400, Arne Vajhøj wrote:

    The main point of ORM is to avoid boilerplate code like iterating and
    result sets and stuff them into the object data structure.

    Those are just up to the DB API, taking advantage of features of the
    language. Like the query iterator I posted elsewhere.

    The point of the ORM is supposed to be to get away from SQL altogether,
    and let you operate in terms of language-native objects.

    Some ORM's operate with a dirty concept and automatically save changed objects.

    How do you map between OO objects and SQL tables? That’s called the “impedance mismatch”, which tends to cause more problems than it solves.

    Some ORM's provide identical syntax for doing things even though the underlying databases have different SQL syntax for it, which help making
    the application more portable.

    More commonly you want to support different programming languages
    accessing the same back-end DBMS containing the common enterprise-wide
    data, rather than move a program between different back-end DBMSes. ORMs
    are language-specific, so they don’t help with that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Fri Jul 11 20:48:22 2025
    On 7/11/2025 8:35 PM, Lawrence D'Oliveiro wrote:
    On Fri, 11 Jul 2025 19:43:59 -0400, Arne Vajhøj wrote:
    The main point of ORM is to avoid boilerplate code like iterating and
    result sets and stuff them into the object data structure.

    Those are just up to the DB API, taking advantage of features of the language. Like the query iterator I posted elsewhere.

    The language can make it more or less cumbersome.

    But the ORM makes it completely go away.

    Even for complex object structures.

    The point of the ORM is supposed to be to get away from SQL altogether,
    and let you operate in terms of language-native objects.

    ORM cannot read minds.

    Somehow the code need to tell the ORM what query to make.

    That us usually ORM QL, SQL or equivalent fluent API.

    But it is:
    some form of query --(ORM)--> object structure
    not:
    some form of query --(application code)--> object structure

    Some ORM's operate with a dirty concept and automatically save changed
    objects.

    How do you map between OO objects and SQL tables?

    There are 3 common approaches: attributes/annotations, config file
    and convention.

    That’s called the “impedance mismatch”, which tends to cause more problems than it solves.

    20 years ago there were some pain points. But solution has been found
    since then.

    Some ORM's provide identical syntax for doing things even though the
    underlying databases have different SQL syntax for it, which help making
    the application more portable.

    More commonly you want to support different programming languages
    accessing the same back-end DBMS containing the common enterprise-wide
    data, rather than move a program between different back-end DBMSes. ORMs
    are language-specific, so they don’t help with that.

    True.

    But it does make it easier to change database. Which also happens
    occasionally.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sat Jul 12 02:01:06 2025
    On Fri, 11 Jul 2025 20:48:22 -0400, Arne Vajhøj wrote:

    But the ORM makes it completely go away.

    And yet ..

    ORM cannot read minds.

    Somehow it manages to be both: it can make the need for low-level SQL management go away, and yet it cannot figure out what language-native
    objects you want to replace it with.

    Somehow the code need to tell the ORM what query to make.

    If you knew what query to make, you wouldn’t need the ORM.

    20 years ago there were some pain points. But solution has been found
    since then.

    I would like to see what some such “solution” might be, given you haven’t offered one yet.

    How about tackling that web query-form example I posted elsewhere? The one
    with the long Python expression for packaging up all the fields with data
    that the user has entered into an appropriate SQL WHERE clause?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to [email protected] on Mon Jul 14 14:32:52 2025
    In article <104s2a2$1nlra$[email protected]>,
    Stephen Hoffman <[email protected]d> wrote:
    On 2025-07-10 12:00:31 +0000, Dan Cross said:

    Most USENET newsgroups used to have an FAQ; it may be useful to restart
    that for comp.os.vms (among others), perhaps with a section on
    well-known trolls.

    Having some familiarity with the topic, I can assure you that a FAQ
    will get ignored. But if you want to post your own FAQ, by all means
    have at.

    For your concerns here, you can plonk the posters that are assisting
    with getting unwanted posters and postings past others' existing
    filters.

    And if your particular newsreader has more modern filtering
    capabilities, there can be other options.

    Yup. That was pretty much my point.

    There are two cases:

    1. People who are unfamiliar with the troll, and respond in good
    faith, but do so out of ignorance. An FAQ _may_ benefit those
    peole.

    2. People who insist on interacting with the troll for whatever
    reason, despite repeated pleas that they stop doing that. It is
    clear that asking those people to "stop feeding the troll" is
    futile, and for that, the only reasonable solution is bringing a
    tool to bear so those parts of the topic tree get auto-plonked.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 16 20:31:12 2025
    On 7/11/2025 7:43 PM, Arne Vajhøj wrote:
    On 7/11/2025 7:35 PM, Lawrence D'Oliveiro wrote:
    On Thu, 10 Jul 2025 20:29:56 -0400, Arne Vajhøj wrote:
    You can save a lot of code by using ORM.

    Example (Groovy and JPA):

    jpadata = em.createQuery("SELECT DISTINCT o FROM OrdersJPA AS o JOIN
    FETCH o.orderLines ol", OrdersJPA.class).getResultList()

    Gives you a list of order objects that each has a list of order lines
    objects.

    I thought the whole point of an ORM was to get away from having to hand-
    construct SQL queries. And yet you have done exactly that.

    That is not SQL but HQL.

    :-)

    Correction.

    It is not HQL but JPQL.

    The "SELECT ..." is identical in HQL and JPQL.

    Both the API call and the naming of variable clearly indicates
    that is JPA not Hibernate API.

    I always test with Hibernate as JPA implementation, but standard
    API!

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Wed Jul 16 20:26:46 2025
    On 7/11/2025 10:01 PM, Lawrence D'Oliveiro wrote:
    On Fri, 11 Jul 2025 20:48:22 -0400, Arne Vajhøj wrote:
    But the ORM makes it completely go away.

    And yet ..

    ORM cannot read minds.

    Somehow it manages to be both: it can make the need for low-level SQL management go away, and yet it cannot figure out what language-native
    objects you want to replace it with.

    Somehow the code need to tell the ORM what query to make.

    If you knew what query to make, you wouldn’t need the ORM.

    You have misunderstood what an ORM provide.

    You need to define your object model.

    You need to specify logical what queries or updates you want to do.

    What the ORM does (as the name implies!) is to handle the mapping
    between the object model and the SQL statements.

    You give it a query and it translate that into one or more SELECT
    statements, read the result and stuff it into the object model.

    You tell it to persist an object and it translate that into one
    or more INSERT statements.

    For complex object models having that stuff done automatically
    save a ton of code.

    It is cutting 50-75% of the database access code. And most business applications has a lot of database access code.

    It is not a silver bullet. There are a couple of caveats.

    1) It only makes sense if you have an object model. If your code
    operate on lists, maps/dictionaries and basic data types, then
    there is no point in ORM.

    2) There need to be at least one on the team with more than
    basic skills in the ORM. Any developer can do the queries
    and updates. But putting the annotations/attributes (or the
    equivalent XML mapping for older ORM) on the domain classes
    require deeper skills for any non-trivial case.

    20 years ago there were some pain points. But solution has been found
    since then.

    I would like to see what some such “solution” might be, given you haven’t
    offered one yet.

    How about tackling that web query-form example I posted elsewhere? The one with the long Python expression for packaging up all the fields with data that the user has entered into an appropriate SQL WHERE clause?

    Query WHERE conditions are handled the same way in SQL and various
    ORM query constructs.

    That is part of the basic ORM stuff that has always been covered.

    What has been added since is stuff like:
    * ability for developers to explicit decide between one JOIN query
    or 1+N queries - that is essential for optimization
    * allowing certain non-object-oriented features, because in practice
    database access code may need those

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 16 20:54:11 2025
    On 7/16/2025 8:41 PM, Arne Vajhøj wrote:
    JVM languages (Java, Groovy, Jython etc.) -----------------------------------------

    No problem.

    JPA API and Hibernate works fine.

    You will need different versions on Alpha (Java 5) and
    Itanium/x86-64 (Java 8).

    I have posted list of jar files before.

    For those that don't like to search:

    Alpha (Java 5):

    antlr-2_7_6.jar
    cglib-2_2.jar
    commons-collections-3_1.jar
    dom4j-1_6_1.jar
    hibernate-jpa-2_0-api-1_0_0_final.jar
    hibernate3.jar
    javassist-3_12_0_ga.jar
    jta-1_1.jar
    slf4j-api-1_6_1.jar
    slf4j-jdk14-1_6_1.jar
    +JDBC driver jar

    Itanium/x86-64 (Java 8):

    javax_persistence-api-2_2.jar
    hibernate-core-5_6_5_Final.jar
    hibernate-commons-annotations-5_1_2_Final.jar
    javax_activation-api-1_2_0.jar
    jboss-transaction-api_1_2_spec-1_1_1_Final.jar
    istack-commons-runtime-3_0_7.jar
    stax-ex-1_8.jar
    txw2-2_3_1.jar
    jboss-logging-3_4_3_Final.jar
    antlr-2_7_7.jar
    byte-buddy-1_12_7.jar
    classmate-1_5_1.jar
    jandex-2_4_2_Final.jar
    +JDBC driver jar

    I am sure there are many other version combinations that
    work, but these I have tested extensively without problems.

    Hibernate 3.6.4 and 5.6.5 are very old and a little old respectively
    (current is 6.6.21 and 7.0.6), but very few are going to miss
    any of the newer features and the Java version limits one
    (6.x requires Java 11+ and 7.x requires Java 17).

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Wed Jul 16 20:41:48 2025
    On 7/16/2025 8:26 PM, Arne Vajhøj wrote:
    What the ORM does (as the name implies!) is to handle the mapping
    between the object model and the SQL statements.

    You give it a query and it translate that into one or more SELECT
    statements, read the result and stuff it into the object model.

    You tell it to persist an object and it translate that into one
    or more INSERT statements.

    For complex object models having that stuff done automatically
    save a ton of code.

    It is cutting 50-75% of the database access code. And most business applications has a lot of database access code.

    It is not a silver bullet. There are a couple of caveats.

    1) It only makes sense if you have an object model. If your code
       operate on lists, maps/dictionaries and basic data types, then
       there is no point in ORM.

    2) There need to be at least one on the team with more than
       basic skills in the ORM. Any developer can do the queries
       and updates. But putting the annotations/attributes (or the
       equivalent XML mapping for older ORM) on the domain classes
       require deeper skills for any non-trivial case.

    And if we try and go back to VMS (this is comp.os.vms!), then
    if we are to look at what ORM is available on VMS.

    JVM languages (Java, Groovy, Jython etc.) -----------------------------------------

    No problem.

    JPA API and Hibernate works fine.

    You will need different versions on Alpha (Java 5) and
    Itanium/x86-64 (Java 8).

    I have posted list of jar files before.

    PHP
    ---

    Doctrine works fine.

    I cannot get composer to work on VMS, but:
    * run composer on PC to require doctrine/orm and symfony/cache
    * ZIP vendor tree
    * transfer and UNZIP on VMS
    works fine.

    Python
    ------

    My preferred ORM for Python is SQLModel. Which is based
    Pydantic and SQLAlchemy.

    It works fine on *nix and Windows.

    But pip install fails on VMS. At least on my VMS system.
    It hangs in some infinite loop creating temporary files.

    I assume that it is due to both Pydantic and SQLAlchemy
    containing native modules.

    My Python module skills are not good enough to fix it.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sat Jul 19 02:44:51 2025
    On Wed, 16 Jul 2025 20:26:46 -0400, Arne Vajhøj wrote:

    You have misunderstood what an ORM provide.

    You need to define your object model.

    You need to specify logical what queries or updates you want to do.

    What the ORM does (as the name implies!) is to handle the mapping
    between the object model and the SQL statements.

    So, if I leave out the ORM, then I don’t need to “define” an “object model” that it can handle, and just go straight to the queries/updates. Sounds like less work, to me.

    You give it a query and it translate that into one or more SELECT
    statements, read the result and stuff it into the object model.

    I’ve seen ORM calls corresponding to more complex queries, like joins. Basically you are just expressing the join in a separate language of equal complexity, with no effort saved that I could see.

    For complex object models having that stuff done automatically save a
    ton of code.

    It is cutting 50-75% of the database access code.

    And just replacing it with a lot of ORM code.

    And most business applications has a lot of database access code.

    I maintain one sizeable application for a client that was written in PHP
    (by a previous developer). They tried to wrap table information in custom classes representing things like employees, work entries, customer
    companies and company contacts. The code was just a convoluted mess,
    adding complexity instead of removing it. In every page representing some database query/update function that I needed to make major changes to, I
    saved code by getting rid of use of these classes and substituting the
    queries and handling the results directly.

    1) It only makes sense if you have an object model. If your code
    operate on lists, maps/dictionaries and basic data types, then there
    is no point in ORM.

    But surely just about all code fits that description.

    2) There need to be at least one on the team with more than
    basic skills in the ORM.

    And if you have different apps written in different languages, all
    accessing a common database (a very common situation), you need someone
    with ORM skills in every language.

    I would like to see what some such “solution” might be, given you
    haven’t offered one yet.

    How about tackling that web query-form example I posted elsewhere? The
    one with the long Python expression for packaging up all the fields
    with data that the user has entered into an appropriate SQL WHERE
    clause?

    Query WHERE conditions are handled the same way in SQL and various ORM
    query constructs.

    Show us.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 23 00:54:01 2025
    On Wed, 16 Jul 2025 20:26:46 -0400, Arne Vajhøj wrote:

    You have misunderstood what an ORM provide.

    You need to define your object model.

    You need to specify logical what queries or updates you want to do.

    What the ORM does (as the name implies!) is to handle the mapping
    between the object model and the SQL statements.

    You give it a query and it translate that into one or more SELECT
    statements, read the result and stuff it into the object model.

    You tell it to persist an object and it translate that into one or more INSERT statements.

    For complex object models having that stuff done automatically save a
    ton of code.

    It is cutting 50-75% of the database access code. And most business applications has a lot of database access code.

    The trouble is, SQL is already a very concise language. So the idea that
    you can add some kind of ORM to, say, Java, a language not known for its conciseness, in order to save code overall, is just absurd.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jul 28 19:28:51 2025
    On 7/18/2025 10:44 PM, Lawrence D'Oliveiro wrote:
    On Wed, 16 Jul 2025 20:26:46 -0400, Arne Vajhøj wrote:
    You have misunderstood what an ORM provide.

    You need to define your object model.

    You need to specify logical what queries or updates you want to do.

    What the ORM does (as the name implies!) is to handle the mapping
    between the object model and the SQL statements.

    So, if I leave out the ORM, then I don’t need to “define” an “object model” that it can handle, and just go straight to the queries/updates. Sounds like less work, to me.

    It is the other way around. If you don't have an object model,
    then you can't use an ORM.

    But having an object model is normal for larger business applications
    today. Java, C#, C++, Python, PHP does not matter.

    You give it a query and it translate that into one or more SELECT
    statements, read the result and stuff it into the object model.

    I’ve seen ORM calls corresponding to more complex queries, like joins. Basically you are just expressing the join in a separate language of equal complexity, with no effort saved that I could see.

    I repeat: the saving is not in the query, but in the mapping from
    the query result to the object model.

    For complex object models having that stuff done automatically save a
    ton of code.

    It is cutting 50-75% of the database access code.

    And just replacing it with a lot of ORM code.

    No.

    The mapping code just goes away and is not replaced with
    anything.

    (well - there is of course code in the ORM framework, but that
    work is shared among millions of applications)

    And most business applications has a lot of database access code.

    I maintain one sizeable application for a client that was written in PHP
    (by a previous developer). They tried to wrap table information in custom classes representing things like employees, work entries, customer
    companies and company contacts. The code was just a convoluted mess,
    adding complexity instead of removing it. In every page representing some database query/update function that I needed to make major changes to, I saved code by getting rid of use of these classes and substituting the queries and handling the results directly.

    Sounds like you should have chosen an ORM instead!

    :-)

    1) It only makes sense if you have an object model. If your code
    operate on lists, maps/dictionaries and basic data types, then there
    is no point in ORM.

    But surely just about all code fits that description.

    It makes sense to keep small applications simple.

    But larger business applications (that are less than 25 years old)
    almost always use an object model.

    2) There need to be at least one on the team with more than
    basic skills in the ORM.

    And if you have different apps written in different languages, all
    accessing a common database (a very common situation), you need someone
    with ORM skills in every language.

    That is a given.

    But it is not a big problem.

    Very few companies have business applications using an object model
    for operational/transactional use of database in multiple languages.

    Too much code duplication, too many constraints with caching,
    too much risk for transactional integrity issues.

    Instead they have one application/service own the database and
    have other applications/services go via that application/service.

    On top of that they may have a number of reporting/analytical
    and/or data transfer applications in other languages also using
    the database. But those do not use an object model and therefore
    have no need for ORM.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 29 01:07:53 2025
    On Mon, 28 Jul 2025 19:28:51 -0400, Arne Vajhøj wrote:

    I repeat: the saving is not in the query, but in the mapping from the
    query result to the object model.

    But I thought a key point of ORMs is hiding the SQL. Look at SQLAlchemy,
    for example <https://docs.sqlalchemy.org/en/20/orm/quickstart.html>, which
    does a lot of this. Is that a bad example of an ORM?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jul 28 22:36:59 2025
    On 7/28/2025 9:07 PM, Lawrence D'Oliveiro wrote:
    On Mon, 28 Jul 2025 19:28:51 -0400, Arne Vajhøj wrote:
    I repeat: the saving is not in the query, but in the mapping from the
    query result to the object model.

    But I thought a key point of ORMs is hiding the SQL.

    It is not.

    The primary point is to save all the code mapping between
    query result and object model.

    Look at SQLAlchemy,
    for example <https://docs.sqlalchemy.org/en/20/orm/quickstart.html>, which does a lot of this. Is that a bad example of an ORM?

    I prefer SQLModel on top of SQLAlchemy, but ...

    Even with ORM you need to express the query.

    There are 3 common ways to express a query in
    an ORM:
    * an ORM specific query language similar to but not identical
    to SQL. Examples: JPQL (Java), HQL (Java), DQL (PHP) etc..
    * plain SQL. Example: MyBatis (Java)
    * fluent API. Examples: EF (.NET), SQLModel and SQLAlchemy
    (Python)

    But even though the ORM framework authors undoubtedly
    have strong opinions about that choice, then in the
    big picture it does not matter.

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0", T1.class).getResultList();

    EF example (fluent API):

    List<T1> res = db.T1.Where(o => o.F > 0).ToList();

    That will both end up executing the SQL:

    SELECT * FROM t1 WHERE f > 0

    It is fundamentally the same query. Just slightly different
    syntax.

    SQLAlchemy have chosen the fluent API model.

    The big difference in the above two examples compared
    to traditional SQL API (JDBC and ADO.NET for Java and .NET)
    is that it return a List<T1> instead of a generic result
    like List<Object[]>.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 29 04:25:53 2025
    On Mon, 28 Jul 2025 22:36:59 -0400, Arne Vajhøj wrote:

    Even with ORM you need to express the query.

    So a lot of the stuff in SQLAlchemy is useless, then?

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0", T1.class).getResultList();

    That seems like a really dumb idea. You see my point about having
    their own query language does nothing to simplify the code.

    The big difference in the above two examples compared to traditional
    SQL API (JDBC and ADO.NET for Java and .NET) is that it return a
    List<T1> instead of a generic result like List<Object[]>.

    Is there a version that returns an iterator? Can be more
    memory-efficient for dealing with lots of returned records.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 29 09:39:51 2025
    On 7/29/2025 12:25 AM, Lawrence D'Oliveiro wrote:
    On Mon, 28 Jul 2025 22:36:59 -0400, Arne Vajhøj wrote:
    Even with ORM you need to express the query.

    So a lot of the stuff in SQLAlchemy is useless, then?

    No.

    It does what it is supposed to do: do the mapping between
    the query result and the object model.

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0",
    T1.class).getResultList();

    That seems like a really dumb idea. You see my point about having
    their own query language does nothing to simplify the code.

    Again: the benefit is not in the query but in the fact that it
    stuff the query result into the object model without any
    application code.

    The big difference in the above two examples compared to traditional
    SQL API (JDBC and ADO.NET for Java and .NET) is that it return a
    List<T1> instead of a generic result like List<Object[]>.

    Is there a version that returns an iterator? Can be more
    memory-efficient for dealing with lots of returned records.

    Usually there are.

    But it is a rare requirement when using object models.

    .NET has one of the easiest ways to do it.

    List<T1> res = db.T1.Where(o => o.F > 0).ToList();
    foreach(T1 o in res)

    will materialize all rows in memory and then iterate over
    it, but:

    foreach(T1 o in db.T1.Where(o => o.F > 0))

    will iterate directly.

    (for JPA you either need to move up to Spring JPA or
    move down to Hibernate to iterate)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Tue Jul 29 10:02:00 2025
    On 7/29/2025 9:39 AM, Arne Vajhøj wrote:
    On 7/29/2025 12:25 AM, Lawrence D'Oliveiro wrote:
    On Mon, 28 Jul 2025 22:36:59 -0400, Arne Vajhøj wrote:
    Even with ORM you need to express the query.

    So a lot of the stuff in SQLAlchemy is useless, then?

    No.

    It does what it is supposed to do: do the mapping between
    the query result and the object model.

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0",
    T1.class).getResultList();

    That seems like a really dumb idea. You see my point about having
    their own query language does nothing to simplify the code.

    Again: the benefit is not in the query but in the fact that it
    stuff the query result into the object model without any
    application code.

    Let me give an example. Python.

    SQL
    ---

    import sqlite3

    from popo_domain import myorder, orderline

    def map_list_rel_to_obj(data):
    res = []
    lastorderid = -1
    for row in data:
    if row[0] != lastorderid:
    res.append(myorder(row[0], row[1], row[2]))
    lastorderid = row[0]
    res[-1].orderlines.append(orderline(row[3], row[4], row[5],
    row[6]))
    return res

    with sqlite3.connect('test.db') as con:
    c = con.cursor()
    c.execute('SELECT myorder.orderid, customer, state, orderlineid,
    itemname, itemprice, quantity FROM myorder JOIN orderline ON
    myorder.orderid = orderline.orderid')
    rdata = c.fetchall()
    odata = map_list_rel_to_obj(rdata)

    SQLModel
    --------

    from sqlmodel import Session, create_engine, select
    from sqlalchemy.orm import joinedload

    from sqm_domain import myorder, orderline

    eng = create_engine('sqlite+pysqlite:///test.db')
    with Session(eng) as ses:
    odata = ses.exec(select(myorder).options(joinedload(myorder.orderlines))).unique(
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jul 29 23:07:44 2025
    On Tue, 29 Jul 2025 09:39:51 -0400, Arne Vajhøj wrote:

    On 7/29/2025 12:25 AM, Lawrence D'Oliveiro wrote:

    On Mon, 28 Jul 2025 22:36:59 -0400, Arne Vajhøj wrote:

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0",
    T1.class).getResultList();

    That seems like a really dumb idea. You see my point about having their
    own query language does nothing to simplify the code.

    Again: the benefit is not in the query but in the fact that it stuff the query result into the object model without any application code.

    So why does it have its own extra mechanism for formulating the query,
    then? You yourself are admitting there is no benefit in that. Yet here you
    are, having to go through that same extra layer of mechanism.

    Is there a version that returns an iterator? Can be more
    memory-efficient for dealing with lots of returned records.

    Usually there are.

    But it is a rare requirement when using object models.

    On the contrary, I use it a lot. Maybe the problem is that object models
    make the idea hard to use.

    .NET has one of the easiest ways to do it.

    List<T1> res = db.T1.Where(o => o.F > 0).ToList();
    foreach(T1 o in res)

    will materialize all rows in memory and then iterate over it, but:

    foreach(T1 o in db.T1.Where(o => o.F > 0))

    will iterate directly.

    Can you explicitly call a “next()” method on an iterator object to
    retrieve each record in turn?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 29 20:56:18 2025
    On 7/29/2025 7:07 PM, Lawrence D'Oliveiro wrote:
    On Tue, 29 Jul 2025 09:39:51 -0400, Arne Vajhøj wrote:

    On 7/29/2025 12:25 AM, Lawrence D'Oliveiro wrote:

    On Mon, 28 Jul 2025 22:36:59 -0400, Arne Vajhøj wrote:

    JPQL example (ORM specific query language):

    List<T1> res = em.createQuery("SELECT o FROM T1 AS o WHERE o.f > 0",
    T1.class).getResultList();

    That seems like a really dumb idea. You see my point about having their
    own query language does nothing to simplify the code.

    Again: the benefit is not in the query but in the fact that it stuff the
    query result into the object model without any application code.

    So why does it have its own extra mechanism for formulating the query,
    then? You yourself are admitting there is no benefit in that. Yet here you are, having to go through that same extra layer of mechanism.

    It is not an extra layer. It is an alternative layer.

    As I said then there are 3 options:
    * ORM specific QL
    * fluent API
    * SQL

    I believe the arguments goes like:
    * ORM specific QL - similar to SQL so easy to learn
    but fit better with the object approach than SQL
    * fluent API - more type safe than a string with a QL
    * SQL - everybody already knows SQL so picking that avoid
    learning a new way

    Different ORM chose differently.

    If I were to make a guess then I would say usage is like
    50% - 40% - 10%.

    (note that some ORM support more than one option)

    If you were to create your own ORM, then feel free to
    make it use SQL.

    It does not really impact the application code that much, because
    no matter which option then the same query need to be expressed.

    Is there a version that returns an iterator? Can be more
    memory-efficient for dealing with lots of returned records.

    Usually there are.

    But it is a rare requirement when using object models.

    On the contrary, I use it a lot. Maybe the problem is that object models
    make the idea hard to use.

    Transactional/operation code use an object model but don't work
    on huge data. If the data is huge, then you paginate.

    Reporting/analytical/data transfer code often work on huge data, but
    you don't use an object model with that.

    .NET has one of the easiest ways to do it.

    List<T1> res = db.T1.Where(o => o.F > 0).ToList();
    foreach(T1 o in res)

    will materialize all rows in memory and then iterate over it, but:

    foreach(T1 o in db.T1.Where(o => o.F > 0))

    will iterate directly.

    Can you explicitly call a “next()” method on an iterator object to retrieve each record in turn?

    If your religious beliefs do not allow you to use foreach loop, then:

    foreach(T1 o in whatever)
    {
    ...
    }

    can be done as:

    IEnumerator<T1> en = whatever.GetEnumerator();
    while(en.MoveNext())
    {
    T1 o = en.Current;
    ...
    }

    But I really don't see any reason to do that.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 30 02:41:59 2025
    On Tue, 29 Jul 2025 20:56:18 -0400, Arne Vajhøj wrote:

    On 7/29/2025 7:07 PM, Lawrence D'Oliveiro wrote:

    So why does it have its own extra mechanism for formulating the query,
    then? You yourself are admitting there is no benefit in that. Yet here
    you are, having to go through that same extra layer of mechanism.

    It is not an extra layer. It is an alternative layer.

    How would you redo your example to not use that layer?

    Transactional/operation code use an object model but don't work on huge
    data. If the data is huge, then you paginate.

    So ORMs are not scalable?

    If your religious beliefs do not allow you to use foreach loop, then:

    foreach(T1 o in whatever)
    {
    ...
    }

    can be done as:

    IEnumerator<T1> en = whatever.GetEnumerator();
    while(en.MoveNext())
    {
    T1 o = en.Current;
    ...
    }

    But I really don't see any reason to do that.

    How would you handle level breaks?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 30 03:02:15 2025
    On Tue, 29 Jul 2025 10:02:00 -0400, Arne Vajhøj wrote:

    def map_list_rel_to_obj(data):
    res = []
    lastorderid = -1
    for row in data:
    if row[0] != lastorderid:
    res.append(myorder(row[0], row[1], row[2]))
    lastorderid = row[0]
    res[-1].orderlines.append(orderline(row[3], row[4], row[5], row[6]))
    return res

    Here’s a somewhat more general way of doing it:

    def group_query(db, table, field_names, cond, break_field) :
    "iterates over records in table, grouped by values of break_field." \
    " Note that table could be a join on multiple tables."
    entries_iter = db_iter \
    (
    db,
    "select %(fields)s from %(table)s where %(cond)s order by %(break)s"
    %
    {
    "fields" : ", ".join(field_names),
    "table" : table,
    "cond" : cond,
    "break" : break_field,
    }
    )
    last_break_id = subbreak = None
    while True :
    entry = next(entries_iter, None)
    if entry != None :
    entry = dict(zip(field_names, entry))
    this_break_id = entry[break_field]
    #end if
    if entry == None or this_break_id != last_break_id :
    if subbreak != None :
    yield subbreak
    if entry == None :
    break
    last_break_id = this_break_id
    subbreak = []
    #end if
    subbreak.append(entry)
    #end while
    #end group_query

    (See my posting elsewhere for the definition of the “db_iter”
    function.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 31 15:16:05 2025
    On 7/29/2025 10:41 PM, Lawrence D'Oliveiro wrote:
    On Tue, 29 Jul 2025 20:56:18 -0400, Arne Vajhøj wrote:
    On 7/29/2025 7:07 PM, Lawrence D'Oliveiro wrote:
    So why does it have its own extra mechanism for formulating the query,
    then? You yourself are admitting there is no benefit in that. Yet here
    you are, having to go through that same extra layer of mechanism.

    It is not an extra layer. It is an alternative layer.

    How would you redo your example to not use that layer?

    If you don't like the QL/QAPI of an ORM, then you pick another ORM.

    Transactional/operation code use an object model but don't work on huge
    data. If the data is huge, then you paginate.

    So ORMs are not scalable?

    It is not a technology limitation.

    It is a functional requirement.

    Nobody want to go to a list page and have it take 30 minutes to
    load a billion rows or make a web service call and get a 1 TB response
    back with a billion rows.

    If your religious beliefs do not allow you to use foreach loop, then:

    foreach(T1 o in whatever)
    {
    ...
    }

    can be done as:

    IEnumerator<T1> en = whatever.GetEnumerator();
    while(en.MoveNext())
    {
    T1 o = en.Current;
    ...
    }

    But I really don't see any reason to do that.

    How would you handle level breaks?

    It is an ordinary while loop and break works like
    it does.

    If you need to break out of two loops, then you would need
    to use a goto as C# does not support break to labeled loops
    like Java does.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 31 15:21:36 2025
    On 7/29/2025 11:02 PM, Lawrence D'Oliveiro wrote:
    On Tue, 29 Jul 2025 10:02:00 -0400, Arne Vajhøj wrote:

    def map_list_rel_to_obj(data):
    res = []
    lastorderid = -1
    for row in data:
    if row[0] != lastorderid:
    res.append(myorder(row[0], row[1], row[2]))
    lastorderid = row[0]
    res[-1].orderlines.append(orderline(row[3], row[4], row[5], row[6]))
    return res

    Here’s a somewhat more general way of doing it:

    def group_query(db, table, field_names, cond, break_field) :
    "iterates over records in table, grouped by values of break_field." \
    " Note that table could be a join on multiple tables."
    entries_iter = db_iter \
    (
    db,
    "select %(fields)s from %(table)s where %(cond)s order by %(break)s"
    %
    {
    "fields" : ", ".join(field_names),
    "table" : table,
    "cond" : cond,
    "break" : break_field,
    }
    )
    last_break_id = subbreak = None
    while True :
    entry = next(entries_iter, None)
    if entry != None :
    entry = dict(zip(field_names, entry))
    this_break_id = entry[break_field]
    #end if
    if entry == None or this_break_id != last_break_id :
    if subbreak != None :
    yield subbreak
    if entry == None :
    break
    last_break_id = this_break_id
    subbreak = []
    #end if
    subbreak.append(entry)
    #end while
    #end group_query

    That does not stuff data into an object model - it is still just
    a list and dict approach.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 31 22:46:36 2025
    On Thu, 31 Jul 2025 15:21:36 -0400, Arne Vajhøj wrote:

    That does not stuff data into an object model - it is still just a list
    and dict approach.

    You did say that the object model is superfluous in this situation.

    Note also the generator function, that yields only one group of records
    with common break field values at a time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Thu Jul 31 22:49:08 2025
    On Thu, 31 Jul 2025 15:16:05 -0400, Arne Vajhøj wrote:

    On 7/29/2025 10:41 PM, Lawrence D'Oliveiro wrote:

    So ORMs are not scalable?

    It is not a technology limitation.

    It is a functional requirement.

    Nobody want to go to a list page and have it take 30 minutes to load a billion rows or make a web service call and get a 1 TB response back
    with a billion rows.

    They could if it’s an API call, though. You might be streaming the entire response back, in that situation.

    How would you handle level breaks?

    It is an ordinary while loop and break works like it does.

    If you need to break out of two loops, then you would need to use a goto
    as C# does not support break to labeled loops like Java does.

    By level break, you want to start a new subgroup in the report every time
    the value in the break field changes. See the example generator function I posted elsewhere, to clarify what I mean.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)