feat(pgrtl): minimal PostgreSQL client RTL (pgxpool + 4 HB_FUNCs)
PG_OPEN(cDsn) -> integer handle, -1 on failure
PG_CLOSE(nH) -> NIL
PG_QUERY(nH, cSQL [, aArgs]) -> array of { col => val } hashes
PG_EXEC (nH, cSQL [, aArgs]) -> rows affected, -1 on error
PG_LAST_ERROR(nH) -> last error string
Backed by github.com/jackc/pgx/v5/pgxpool, which is already in Five's
indirect dep tree (pgserver uses pgproto3 from the same repo). Pool
limits: MaxConns 8, MinConns 1, 5-min idle. Query timeout is capped at
30s so a runaway query can't pin a goroutine forever.
aArgs uses standard Postgres $1/$2/... placeholders — pgx parameter
binding prevents SQL injection. Never concatenate user input into cSQL.
Smoke-tested with app/pg_test.prg: bad DSN returns -1 cleanly (no
panic), the error path prints the expected fallback message, and the
real round-trip path is wired so setting LABDB_DSN to a live database
exercises SELECT + parameter binding + multi-row return without any
further code change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
45
app/pg_test.prg
Normal file
45
app/pg_test.prg
Normal file
@@ -0,0 +1,45 @@
|
||||
// app/pg_test.prg — pgrtl RTL smoke test.
|
||||
//
|
||||
// Tries to connect to a local Postgres; if none is up the test still
|
||||
// passes by checking the failure path. With LABDB_DSN set in the
|
||||
// environment it runs a real round-trip query against labdb.
|
||||
FUNCTION Main()
|
||||
LOCAL cDsn := hb_GetEnv( "LABDB_DSN", "postgres://nope:nope@127.0.0.1:1/nope" )
|
||||
LOCAL nH, aRows, n, hRow, i
|
||||
|
||||
? "Trying:", cDsn
|
||||
nH := PG_OPEN( cDsn )
|
||||
? "PG_OPEN handle:", nH
|
||||
|
||||
IF nH < 0
|
||||
? "(no live Postgres at that DSN — expected when LABDB_DSN unset)"
|
||||
RETURN NIL
|
||||
ENDIF
|
||||
|
||||
? "--- SELECT 1 round trip ---"
|
||||
aRows := PG_QUERY( nH, "SELECT 1 AS one, 'hello'::text AS greet, NULL AS empty" )
|
||||
IF aRows == NIL
|
||||
? "PG_QUERY failed:", PG_LAST_ERROR( nH )
|
||||
PG_CLOSE( nH )
|
||||
RETURN NIL
|
||||
ENDIF
|
||||
? "rows:", Len( aRows )
|
||||
FOR i := 1 TO Len( aRows )
|
||||
hRow := aRows[ i ]
|
||||
? " one :", hRow[ "one" ], ValType( hRow[ "one" ] )
|
||||
? " greet:", hRow[ "greet" ], ValType( hRow[ "greet" ] )
|
||||
? " empty:", hRow[ "empty" ], ValType( hRow[ "empty" ] )
|
||||
NEXT
|
||||
|
||||
? "--- parameter binding ---"
|
||||
aRows := PG_QUERY( nH, "SELECT $1::int AS n, $2::text AS s", { 42, "answer" } )
|
||||
IF aRows != NIL .AND. Len( aRows ) > 0
|
||||
? " n:", aRows[ 1 ][ "n" ]
|
||||
? " s:", aRows[ 1 ][ "s" ]
|
||||
ELSE
|
||||
? "param query failed:", PG_LAST_ERROR( nH )
|
||||
ENDIF
|
||||
|
||||
PG_CLOSE( nH )
|
||||
? "closed."
|
||||
RETURN NIL
|
||||
Reference in New Issue
Block a user