Menu

SVN-Code Commit Log


Commit Date  
[r13059] by mikeaubury

dotnet: the SQL and report APIs generated code actually targets

A generated module referred to a SQL and report API that had never been
written on the .NET side: SQL.*, ASqlCursor, ASqlBinding, ASqlResult,
ASqlException, sqlca, BaseFGLReport, and the aggCount/aggSum family.
Compilation stopped at the first missing type, which hid the rest - so
what looked like two errors was ninety-one once the first two were
stubbed out.

Runtime, all built on the existing FglSql/FglCursor/FglRow layer:

ASqlBinding an ordered value list - the generator uses the same
construct for SQL host variables, DISPLAY and INPUT
ASqlResult one row, with GetData in both the by-ordinal and the
out-parameter form the generator emits
ASqlCursor a declared cursor, with the four SCROLL/WITH HOLD
combinations and the insert cursor
ASql the throwing face of FglSql: generated code wraps every
statement in a try/catch built from the WHENEVER state,
which only works if the operation raises
BaseFGLReport the page engine, driven through events rather than
inheritance so one module can declare several reports
agg* COUNT, SUM, AVG, MIN, MAX and PERCENT

SQL and sqlca are instance properties on FglModule rather than statics,
so the connection and status area stay on the session and two programs
can run in one process.

Generator fixes found while doing it:

- DECLARE SCROLL CURSOR WITH HOLD emitted ScrollCursor and plain
SCROLL CURSOR emitted ScrollCursorWithHold - the two branches were
swapped. The non-scroll branch below was already right.
- PAGENO and LINENO were emitted bare, referring to identifiers that
were never declared. They only mean anything inside a report, so
they now name the report's driver.
- USING emitted a bare Using(), unreachable from a nested report
class; it now lowers to the runtime's formatter, which also grew a
date-mask path since USING applies to DATE as well as to numbers.
- START REPORT ... WITH PAGE LENGTH / MARGINS lowered to setter calls
on the report object that were never emitted. They are now, and
they ignore the -1 the generator passes for an option the source
did not give - otherwise a report that set margins in its OUTPUT
section had them wiped the moment it started.
- WITH TOP OF PAGE called setTopMargin. Copy-paste.

Two page-layout details came out of diffing against the C runtime
rather than from reading it: the page trailer sits against the bottom
margin rather than directly under the last body line, and its height
has to be measured before the first page is laid out or the body takes
one row too many and the break lands in the wrong place. The trailer is
run into a buffer for both.

A 4GL report - page header, ON EVERY ROW, PAGE TRAILER with PAGENO, ON
LAST ROW with COUNT(*) and SUM(), USING masks, margins and page length
- now produces byte-identical output through both toolchains across
three pages, and is added to the end-to-end probe as report.4gl.

Fidelity: 27 semantics, 8 UI, 30 USING, the 66-line report, and 5
end-to-end programs all agree with C. 203 runtime tests pass.

2026-09-02 16:44:01 Tree
[r13058] by mikeaubury

lex_cs: emit the report driver margins as valid C#

print_output_rep_normal() emitted the five REPORT margin assignments as a
bare 'Driver.topMargin=' followed by real_print_expr(), with no
terminator - and printc() puts each fragment on its own indented line
unless newlines are suppressed, so the generated source came out as

Driver.topMargin=
1
Driver.bottomMargin=
1

Driver.rightMargin was also missing its '=' entirely. Every REPORT with
an OUTPUT section therefore failed to compile: 36 errors in the one
demo program that has one, and it was the only thing still stopping it.

Factored the repetition into print_margin_assign(), which wraps the
expression in set_nonewlines()/clr_nonewlines() so each assignment is
one statement on one line. Also dropped a duplicated Driver.outputLoc
emission - it was written twice, the second copy without the stdout
else-branch.

d4_orders.4gl now generates and compiles clean. Fidelity probes still
agree with the C runtime (27 semantics, 8 UI, 30 USING, 66-line report,
4 end-to-end programs) and the 203 runtime tests pass.

2026-09-02 14:49:07 Tree
[r13057] by mikeaubury

configure: restore --with-pg-all, and find PostgreSQL where RHEL puts it

Reported: PostgreSQL is no longer detected from SVN, while the released
tarball detects it and builds. Alma Linux 8, PostgreSQL 18, invoked as

./configure --with-pg-all=/usr/pgsql-18

--with-pg-all was a shorthand in the old configure.in that pointed every
PostgreSQL component at one prefix. The rewrite dropped it, and autoconf
only WARNS about an unrecognised --with-... - so configure completed,
the option did nothing, and PostgreSQL silently went missing. It is
restored here, setting --with-postgresql and --with-ecpg unless those
were named explicitly.

This did not show up on Debian or Ubuntu because libpq.pc is on the
default pkg-config path there, so detection succeeded anyway and masked
the ignored option. On RHEL, Alma and Rocky the PGDG packages install
under /usr/pgsql-NN with nothing on the default PATH or PKG_CONFIG_PATH,
so both pkg-config and a bare pg_config come up empty.

So detection also gained a fallback chain: pkg-config, then pg_config on
PATH, then pg_config under /usr/pgsql-*, /usr/local/pgsql-* and /opt/*
newest first, then a direct search for libpq-fe.h in the usual include
directories. Whatever answers is then verified by compiling against
libpq-fe.h and linking PQconnectdb, so a stale pg_config reports "not
found" rather than breaking the build later.

Verified: --with-pg-all against a /usr/pgsql-18 style prefix gives
-I<prefix>/include and -L<prefix>/lib -lpq; and with pkg-config unable
to see libpq, the pg_config fallback still finds it.

2026-09-02 14:21:45 Tree
[r13056] by mikeaubury

Update the vendored SQLite3 ODBC driver from 2007 to 0.99991

The vendored copy dated from 13 May 2007 (r9422) and segfaulted on any
statement carrying a bind parameter - which is nearly every statement a
4GL program issues - so SELECT ... WHERE id = lv_id or INSERT with
variables crashed while all-literal statements worked.

Two causes, both long since fixed upstream:

It built its SQL by textual substitution, calling
sqlite3_vmprintf(s->query, (char *) params) - an array of char * cast to
a va_list. That is valid on i386, where a va_list is effectively a
pointer to the stack arguments, and invalid on x86-64, where it is a
struct of register save area offsets. GCC 14 rejects the cast outright,
which is why this file was already built with the diagnostic switched
off. Upstream replaced the whole mechanism with sqlite3_bind_*() on
prepared statements years ago.

Its header was also pinned to sqlite3-local.h, which declares SQLite
3.0.8 (2004), while the plug-in links the system libsqlite3 3.45.1.
Anything added since 3.0.8 - sqlite3_prepare_v2, sqlite3_malloc - was
implicitly declared and had its returned pointer truncated on a 64 bit
build.

Upstream's sqlite3odbc.c and .h replace both files, keeping the small
Aubit preamble (the A4GL_debug shim and #define AUBIT). Three build
flags follow upstream's renames: WITHOUT_DRIVERMGR became
USE_DLOPEN_FOR_GPPS, WITHOUT_WINTERFACE avoids needing unixodbc-dev, and
the in-tree ODBC header gains SQL_C_WCHAR, a standard constant it
predates.

Parameterised statements, cursors and quote escaping now all work.

2026-09-02 13:17:26 Tree
[r13055] by mikeaubury

Generator: unblock report modules and fix generated label names

Measured against fgldemo - 41 modules, 5900 lines - rather than guessed
at. Two fixes, both found by trying to compile it:

binding.o was never in the plug-in's link line, and binding.c is in any
case wrapped entirely in #ifdef CRAP, so A4GL_dtype_sz was never
compiled even though compile_cs_sql.c calls it. Every REPORT module
failed at dlopen with an undefined symbol. The guard now closes before
the dtparts table, leaving decode_datetime and A4GL_dtype_sz live - the
only two the caller needs - and the two SPRINTF macros in them, whose
header is also inside the disabled part, are plain snprintf now.

Generated labels used %d on a value that can be negative, producing
continue_input_-908174256, which is not a C# identifier. Now %u.

fgldemo now generates 39 of 41 modules, up from 36. The two that do not
are LOAD/UNLOAD, which are assertion stubs in the generator, and
DROP DATABASE, which Aubit refuses everywhere and is not a C# issue.

2026-09-02 11:58:39 Tree
[r13054] by mikeaubury

.NET: 4GL's implicit conversions, display widths, and a README

4GL allows LET lv_char = lv_int and converts on the way; C# does not. The
generator already resolves this at compile time, emitting an explicit
conversion only where the declared types differ - LET b = a between two
INTEGERs still emits a plain assignment. This adds the conversions it
emits: AsChar, AsString, AsInt, AsSmallInt, AsBigInt, AsDecimal, AsFloat,
AsSmallFloat, AsBool, AsDate, AsDateTime, AsMonthSpan and AsTimeSpan.

They are hand-written rather than System.Convert, which is wrong in the
three places that matter and has a test spelling it out: Convert rounds
7.9 to 8 where 4GL truncates to 7, turns NULL into 0 where 4GL keeps
NULL, and throws on "abc" where 4GL yields NULL.

DISPLAY now passes each value's width, not just a DECIMAL's precision.
4GL prints an INTEGER in 11 columns, a SMALLINT in 6, a FLOAT in 14 - and
a NULL still occupies its field, which a boxed null cannot know on its
own. CHAR is deliberately excluded: it is already padded at assignment,
and widening it again would defeat CLIPPED.

A fifth end-to-end program, convert.4gl, covers thirteen cross-type
assignments including truncation of negatives and NULL propagation. All
four end-to-end programs and the other four probes pass.

Adds dotnet/README.md.

2026-09-02 11:36:42 Tree
[r13053] by mikeaubury

Generator: carry DECIMAL precision and scale; control flow probe

Same problem as CHAR, same shape of fix. A DECIMAL(10,2) maps to C#'s
decimal, which carries neither the declared precision nor the scale, so
2.5 printed where 4GL prints 2.50, and it printed bare where 4GL right
aligns it in 12 columns.

Both are supplied where the declaration is still known. The assignment
wrapper - already used for CHAR - now also emits

L_d = Fgl.Scale(expr, 10, 2);

which rounds to the declared scale and forces the trailing zeros, since
Math.Round reduces a scale but never extends one. The width is the
declared PRECISION plus two and is passed at the DISPLAY site, where
expr_datatype still knows it:

Display("div =", Fgl.Disp(L_d, 10));

Adds IsNull, AsDecimal and CompareString to the runtime, which generated
code calls unqualified.

A third end-to-end probe covers FOR, WHILE, IF, CASE, arithmetic, string
concatenation, a function with a return value, a compound condition and
IS NULL. All three end-to-end probes pass, as do the other four and the
188 unit tests.

2026-09-02 07:59:22 Tree
[r13052] by mikeaubury

Generator: carry CHAR widths through to the generated code

A 4GL CHAR is fixed width and blank padded, but it maps to a C# string,
which has no width - so LET c = "hello" on a CHAR(10) was losing the
padding and DISPLAY printed five columns instead of ten.

The width is known at the assignment even though it is not recoverable
from the C# type, so every assignment to a CHAR is now wrapped:

L_s = Fgl.Pad("hello", 10);

This uses the generator's existing write-prefix / suffix mechanism in
decode_varbind, so it costs one helper and two small branches rather
than switching the emitted type to FglChar - which would have touched
the declaration, LET and parameter paths.

VARCHAR is deliberately excluded: it has no fixed width to pad to.

Also adds Fgl.CompareString, which generated code calls wherever either
side of a comparison is a string. Trailing blanks are not significant,
and a comparison involving NULL is false - including NULL against NULL.

A second end-to-end probe covers nine assignment forms: CHAR padding and
truncation, CHAR from CHAR, VARCHAR, an array element, a record field,
NULL, and an INTEGER's display width. Both probes pass.

2026-09-02 07:53:54 Tree
[r13051] by mikeaubury

Add the C# generator plug-in source (lex_cs)

lex_cs was copied into the tree but never added to SVN, so it - and the
retargeting in r13050 - existed only in a working copy. This adds the 16
source files the plug-in builds from, 432K in total.

Deliberately NOT added: the directory also holds about 35M of backup
copies (latest/, 250210/, CM/, old1/, backup_221009/), seven copies of
the same 1.7M tarball, and .exe/.dll/.mdb binaries including an Informix
DLL. Those are ignored rather than versioned; they are still on disk if
any of them turn out to matter.

The plug-in is still not built by the top-level make - it has to be
built by hand in lib/liblex/lex_cs. Wiring it in is a separate decision,
since it would make every build depend on it.

2026-09-02 07:45:49 Tree
[r13050] by mikeaubury

Retarget the C# generator onto the .NET runtime

The CSNEW generator already built and worked - it was simply never wired
into the top-level build. Retargeting it turned out to be a handful of
edits rather than a rewrite:

namespace and using -> Aubit4GL / Aubit4GL.Generated
base class -> FglModule, which carries the session
DATE -> DateOnly, an exact match
DECIMAL and MONEY -> decimal (this is where unknown_8 dies)
DATETIME, INTERVAL -> FglDateTime, FglInterval
BYTE and TEXT -> byte[]
SMALLINT -> short, not int
CLIPPED, LENGTH,
UPSHIFT and friends -> Fgl.*

DISPLAY now emits its expressions as separate arguments rather than
concatenating them, because 4GL renders each value at its type's own
width - an INTEGER in 11 columns, a SMALLINT in 6 - and joining them
into one string first threw that away. SMALLINT mapping to int? had the
same effect and is fixed.

A new end-to-end probe compiles ONE 4GL program through BOTH toolchains
and diffs what they print. It is the only check that exercises the
generator and the runtime together; every other probe tests a
hand-written .NET side. It passes.

Known gap, recorded at the head of the probe: CHAR(n) maps to a bare
string, so its declared width is lost and an unclipped CHAR prints
without its padding. Fixing that means carrying the width into the
declaration and routing assignment through Assign(), which touches the
declaration, LET and parameter paths.

2026-09-02 07:44:09 Tree
Older >