====== std.db.postgres ======
PostgreSQL-Client (Wire-Protokoll v3): Pure-Lyx-Implementierung ohne ''libpq'' — direktes TCP + PostgreSQL Frontend/Backend Protocol 3.0. Unterstützt Simple Query, Prepared Statements mit Parametern, Transaktionen, Connection Pooling, LISTEN/NOTIFY und COPY. Authentifizierung: MD5, Cleartext (kein SASL).
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:db:sqlite|std.db.sqlite]] · [[lyx_-_programmiersprache:units:db:mysql|std.db.mysql]] · [[lyx_-_programmiersprache:units:crypto:md5|std.crypto.md5]]
----
===== Konstanten =====
==== Verbindungsstatus ====
Funktionen für Verbindungsaufbau und -verwaltung:
^ Konstante ^ Wert ^ Beschreibung ^
| ''PG_STATUS_DISCONNECTED'' | 0 | Nicht verbunden |
| ''PG_STATUS_CONNECTED'' | 1 | Verbunden, bereit für Queries |
| ''PG_STATUS_IN_TX'' | 2 | Aktive Transaktion |
| ''PG_STATUS_TX_ERROR'' | 3 | Transaktion im Fehlerzustand |
==== Fehlercodes ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''PG_ERR_NONE'' | 0 | Kein Fehler |
| ''PG_ERR_SOCKET'' | 3001 | Socket-Fehler |
| ''PG_ERR_CONNECT'' | 3002 | Verbindungsfehler |
| ''PG_ERR_OOM'' | 3003 | Kein Speicher |
| ''PG_ERR_SEND'' | 3004 | Sende-Fehler |
| ''PG_ERR_RECV'' | 3005 | Empfangs-Fehler |
| ''PG_ERR_PROTO'' | 3006 | Protokollfehler / Server-Fehlermeldung |
==== Transaktionsstatus (ReadyForQuery) ====
^ Konstante ^ Wert ^ Beschreibung ^
| ''PG_TX_IDLE'' | 73 (''I'') | Keine aktive Transaktion |
| ''PG_TX_ACTIVE'' | 84 (''T'') | Transaktion aktiv |
| ''PG_TX_ERROR'' | 69 (''E'') | Transaktion im Fehlerzustand |
==== PostgreSQL OID-Typen ====
^ Konstante ^ OID ^ Beschreibung ^
| ''PG_OID_BOOL'' | 16 | Boolean |
| ''PG_OID_BYTEA'' | 17 | Binärdaten |
| ''PG_OID_INT8'' | 20 | BIGINT |
| ''PG_OID_INT2'' | 21 | SMALLINT |
| ''PG_OID_INT4'' | 23 | INTEGER |
| ''PG_OID_TEXT'' | 25 | TEXT |
| ''PG_OID_FLOAT4'' | 700 | REAL |
| ''PG_OID_FLOAT8'' | 701 | DOUBLE PRECISION |
| ''PG_OID_VARCHAR'' | 1043 | VARCHAR |
| ''PG_OID_DATE'' | 1082 | DATE |
| ''PG_OID_TIMESTAMP'' | 1114 | TIMESTAMP |
| ''PG_OID_NUMERIC'' | 1700 | NUMERIC/DECIMAL |
==== Protokoll-Nachrichtentypen ====
Backend → Client (Auswahl): ''PG_MSG_AUTH'' (R), ''PG_MSG_READY'' (Z), ''PG_MSG_ROW_DESC'' (T), ''PG_MSG_DATA_ROW'' (D), ''PG_MSG_CMD_COMPLETE'' (C), ''PG_MSG_ERROR'' (E), ''PG_MSG_NOTICE'' (N), ''PG_MSG_NOTIFY'' (A), ''PG_MSG_COPY_IN'' (G), ''PG_MSG_COPY_OUT'' (H).
Frontend → Server (Auswahl): ''PG_FE_QUERY'' (Q), ''PG_FE_PARSE'' (P), ''PG_FE_BIND'' (B), ''PG_FE_EXECUTE'' (E), ''PG_FE_SYNC'' (S), ''PG_FE_TERMINATE'' (X).
----
===== Funktionen =====
==== Verbindung ====
Funktionen für Verbindungsaufbau und -verwaltung:
^ Signatur ^ Beschreibung ^
| ''PGConnect(host: pchar, port: int64, user: pchar, password: pchar, database: pchar): int64'' | Öffnet TCP-Verbindung und führt Handshake durch. Gibt PGConn-Pointer zurück; 0 nur bei TCP-Fehler. Bei Auth-Fehler: Pointer gültig, aber ''PGIsConnected''=0 und ''PGError'' gesetzt |
| ''PGClose(conn: int64): void'' | Sendet Terminate-Nachricht, schließt TCP, gibt PGConn-Speicher frei |
| ''PGIsConnected(conn: int64): int64'' | Gibt 1 zurück wenn verbunden und bereit |
| ''PGError(conn: int64): pchar'' | Fehlermeldung der letzten Operation (SQLSTATE oder Text) |
| ''PGErrno(conn: int64): int64'' | Fehlercode der letzten Operation (PG_ERR_*) |
| ''PGServerVersion(conn: int64): pchar'' | Server-Versionsstring (z. B. ''„16.2"'') |
| ''PGBackendPID(conn: int64): int64'' | PID des Server-Prozesses |
| ''PGGetHost(conn: int64): pchar'' | Host aus dem Connect-Aufruf |
| ''PGGetPort(conn: int64): int64'' | Port aus dem Connect-Aufruf |
| ''PGGetUser(conn: int64): pchar'' | Benutzername |
| ''PGGetDatabase(conn: int64): pchar'' | Datenbankname |
| ''PGGetTxStatus(conn: int64): int64'' | Transaktionsstatus (''PG_TX_IDLE''/''PG_TX_ACTIVE''/''PG_TX_ERROR'') |
| ''PGPing(conn: int64): int64'' | Prüft ob die Verbindung noch aktiv ist. Gibt 1 zurück wenn der Server antwortet |
==== Simple Query ====
^ Signatur ^ Beschreibung ^
| ''PGQuery(conn: int64, sql: pchar): int64'' | Sendet SQL-Statement, liest alle Antworten und gibt PGResult-Pointer zurück; 0 bei Fehler |
| ''PGFreeResult(result: int64): void'' | Gibt alle Ressourcen des PGResult frei |
==== Result-Accessoren ====
^ Signatur ^ Beschreibung ^
| ''PGNumRows(result: int64): int64'' | Anzahl zurückgelieferter Zeilen |
| ''PGNumFields(result: int64): int64'' | Anzahl Spalten |
| ''PGAffectedRows(result: int64): int64'' | Bei INSERT/UPDATE/DELETE: Anzahl betroffener Zeilen |
| ''PGFetchRow(result: int64): int64'' | Bewegt den Cursor zur nächsten Zeile. Gibt 1 wenn eine Zeile verfügbar, 0 wenn fertig |
| ''PGGetStr(result: int64, col: int64): pchar'' | Spalte als String (0-basiert). 0 wenn NULL |
| ''PGGetInt(result: int64, col: int64): int64'' | Spalte als int64 (Text → int64) |
| ''PGGetFloat(result: int64, col: int64): f64'' | Spalte als f64 (Text → f64) |
| ''PGGetBool(result: int64, col: int64): int64'' | Spalte als Boolean: ''„t"''/''„T"''/''„1"'' → 1, sonst 0 |
| ''PGIsNull(result: int64, row: int64, col: int64): int64'' | 1 wenn Wert NULL ist (direkter Row-Index, nicht Cursor) |
| ''PGGetFieldName(result: int64, col: int64): pchar'' | Spaltenname |
| ''PGGetFieldTypeOid(result: int64, col: int64): int64'' | PostgreSQL-Typ-OID (vgl. PG_OID_*-Konstanten) |
| ''PGDataSeek(result: int64, row: int64)'' | Setzt den Cursor auf Zeile ''row'' (für erneutes Lesen) |
==== Prepared Statements ====
Funktionen für Prepared Statements:
^ Signatur ^ Beschreibung ^
| ''PGStmtPrepare(conn: int64, name: pchar, sql: pchar): int64'' | Kompiliert SQL-Statement auf dem Server. ''name'': eindeutiger Statement-Name (leer = unbenannt). Gibt PGStmt-Pointer zurück; 0 bei Fehler |
| ''PGStmtExecute(conn: int64, stmt: int64): int64'' | Sendet Bind + Execute + Sync, liest Ergebnisse. Gibt PGResult zurück |
| ''PGStmtClose(conn: int64, name: pchar): void'' | Gibt das benannte Statement auf dem Server frei |
| ''PGStmtDescribe(conn: int64, name: pchar): int64'' | Gibt Metadaten (Parameter- und Spaltentypen) zurück |
| ''PGStmtReset(stmt: int64): void'' | Setzt Parameter-Bindings auf NULL zurück |
| ''PGStmtFree(stmt: int64): void'' | Gibt den lokalen PGStmt-Speicher frei (kein Server-Close) |
==== Parameter-Binding (0-basierter Index) ====
^ Signatur ^ Beschreibung ^
| ''PGBindInt(stmt: int64, i: int64, v: int64)'' | Bindet ''int64'' an Parameter ''i'' (als Dezimalstring) |
| ''PGBindFloat(stmt: int64, i: int64, v: f64)'' | Bindet ''f64'' an Parameter ''i'' |
| ''PGBindStr(stmt: int64, i: int64, v: pchar)'' | Bindet String an Parameter ''i'' |
| ''PGBindBool(stmt: int64, i: int64, v: int64)'' | Bindet Boolean an Parameter ''i'' (0 → ''„f"'', sonst → ''„t"'') |
| ''PGBindNull(stmt: int64, i: int64)'' | Bindet NULL an Parameter ''i'' |
==== Transaktionen ====
Transaktionsverwaltung:
^ Signatur ^ Beschreibung ^
| ''PGBegin(conn: int64): int64'' | BEGIN |
| ''PGCommit(conn: int64): int64'' | COMMIT |
| ''PGRollback(conn: int64): int64'' | ROLLBACK |
| ''PGBeginReadOnly(conn: int64): int64'' | BEGIN READ ONLY |
| ''PGBeginSerializable(conn: int64): int64'' | BEGIN ISOLATION LEVEL SERIALIZABLE |
| ''PGSavepoint(conn: int64, name: pchar): int64'' | SAVEPOINT name |
| ''PGRollbackTo(conn: int64, name: pchar): int64'' | ROLLBACK TO name |
| ''PGReleaseSavepoint(conn: int64, name: pchar): int64'' | RELEASE SAVEPOINT name |
| ''PGInTransaction(conn: int64): int64'' | 1 wenn eine Transaktion aktiv ist |
| ''PGTxFailed(conn: int64): int64'' | 1 wenn die Transaktion im Fehlerzustand ist |
| ''PGSetAutoCommit(conn: int64, on: int64): int64'' | Autocommit ein- oder ausschalten |
==== Metadaten & Hilfsfunktionen ====
^ Signatur ^ Beschreibung ^
| ''PGChanges(conn: int64): int64'' | Betroffene Zeilen des letzten INSERT/UPDATE/DELETE |
| ''PGLastInsertId(conn: int64): int64'' | OID der zuletzt eingefügten Zeile (nur bei INSERT … RETURNING OID) |
| ''PGEscapeStr(dst: int64, src: pchar): int64'' | Escaped einen String für sichere Einbettung in SQL (''''' → ''''''). Gibt Länge zurück |
| ''PGTableExists(conn: int64, table: pchar): int64'' | 1 wenn die Tabelle im Schema existiert |
| ''PGColumnExists(conn: int64, table: pchar, column: pchar): int64'' | 1 wenn die Spalte in der Tabelle existiert |
| ''PGDropTable(conn: int64, table: pchar): int64'' | DROP TABLE IF EXISTS mit Sicherheits-Check |
==== Connection Pool ====
^ Signatur ^ Beschreibung ^
| ''PGPoolCreate(size: int64): int64'' | Erstellt einen Pool mit ''size'' Verbindungsslots. Gibt Pool-Pointer zurück |
| ''PGPoolDestroy(pg: int64): void'' | Schließt alle Verbindungen und gibt Pool-Speicher frei |
| ''PGPoolAcquire(pg: int64, host: pchar, port: int64, user: pchar, password: pchar, database: pchar): int64'' | Gibt eine freie Verbindung zurück (oder öffnet eine neue). Blockiert bis ein Slot frei ist |
| ''PGPoolRelease(pg: int64, conn: int64): void'' | Gibt eine Verbindung an den Pool zurück |
==== LISTEN / NOTIFY ====
Asynchrone Benachrichtigungen über LISTEN/NOTIFY:
^ Signatur ^ Beschreibung ^
| ''PGListen(conn: int64, channel: pchar): int64'' | Registriert den Client für Benachrichtigungen auf ''channel'' |
| ''PGUnlisten(conn: int64, channel: pchar): int64'' | Deregistriert den Client vom ''channel'' |
| ''PGNotify(conn: int64, channel: pchar, payload: pchar): int64'' | Sendet eine Benachrichtigung mit optionalem Payload |
| ''PGWaitNotification(conn: int64, timeout_ms: int64): int64'' | Wartet auf eine Benachrichtigung. Gibt Notification-Pointer zurück oder 0 bei Timeout |
| ''PGGetNotification(conn: int64): int64'' | Prüft nicht-blockierend ob eine Benachrichtigung vorliegt |
| ''PGFreeNotification(notif: int64): void'' | Gibt den Notification-Speicher frei |
| ''PGNotifPid(notif: int64): int64'' | PID des sendenden Prozesses |
| ''PGNotifChannel(notif: int64): pchar'' | Kanal-Name der Benachrichtigung |
| ''PGNotifPayload(notif: int64): pchar'' | Payload der Benachrichtigung (leer wenn kein Payload) |
==== COPY ====
^ Signatur ^ Beschreibung ^
| ''PGCopyBegin(conn: int64, table: pchar, columns: pchar): int64'' | Startet einen ''COPY … FROM STDIN'' Bulk-Import. Gibt 0 zurück bei Fehler |
| ''PGCopyRow(conn: int64, values: int64, count: int64): int64'' | Sendet eine Zeile (CSV-formatiert). ''values'': Zeiger auf Array von ''count'' pchar-Pointern |
| ''PGCopyEnd(conn: int64): int64'' | Beendet den COPY-Stream und bestätigt |
| ''PGCopyAbort(conn: int64, errmsg: pchar): int64'' | Bricht den COPY-Stream mit einer Fehlermeldung ab |
----
===== Verwendung =====
==== Verbindung aufbauen und Query ausführen ====
Funktionen für Verbindungsaufbau und -verwaltung:
import std.db.postgres;
import std.io;
fn Main(): void {
var conn := PGConnect("127.0.0.1", 5432, "app_user", "secret", "mydb");
if (conn == 0 || !PGIsConnected(conn)) {
PrintLn("Verbindung fehlgeschlagen: " + PGError(conn));
if (conn != 0) { PGClose(conn); }
return;
}
PrintLn("Verbunden mit PostgreSQL " + PGServerVersion(conn));
var result := PGQuery(conn, "SELECT id, name, email FROM users LIMIT 10");
if (result == 0) {
PrintLn("Query-Fehler: " + PGError(conn));
PGClose(conn);
return;
}
while (PGFetchRow(result) != 0) {
PrintLn("");
PrintLn(IntToStr(PGGetInt(result, 0))); + " | " + PGGetStr(result, 1) + " | " + PGGetStr(result, 2
}
PGFreeResult(result);
PGClose(conn);
}
==== Prepared Statement mit Parametern ====
import std.db.postgres;
import std.io;
fn InsertUser(conn: int64, name: pchar, email: pchar): int64 {
var stmt := PGStmtPrepare(conn, "ins_user",
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id");
if (stmt == 0) { return -1; }
PGBindStr(stmt, 0, name);
PGBindStr(stmt, 1, email);
var result := PGStmtExecute(conn, stmt);
var newId: int64 := -1;
if (result != 0) {
PGFetchRow(result);
newId := PGGetInt(result, 0);
PGFreeResult(result);
}
PGStmtClose(conn, "ins_user");
PGStmtFree(stmt);
return newId;
}
==== Transaktion mit Savepoint ====
import std.db.postgres;
fn TransferFunds(conn: int64, fromId: int64, toId: int64, amount: int64): bool {
PGBegin(conn);
var r1 := PGQuery(conn, "UPDATE accounts SET balance = balance - 100 WHERE id = 1");
if (r1 == 0) { PGRollback(conn); return false; }
PGFreeResult(r1);
PGSavepoint(conn, "after_debit");
var r2 := PGQuery(conn, "UPDATE accounts SET balance = balance + 100 WHERE id = 2");
if (r2 == 0) {
PGRollbackTo(conn, "after_debit");
PGRollback(conn);
return false;
}
PGFreeResult(r2);
PGCommit(conn);
return true;
}
==== LISTEN / NOTIFY ====
Asynchrone Benachrichtigungen über LISTEN/NOTIFY:
import std.db.postgres;
import std.io;
fn WatchChannel(conn: int64): void {
PGListen(conn, "job_queue");
var notif := PGWaitNotification(conn, 5000); // 5 Sekunden Timeout
if (notif != 0) {
PrintLn("Benachrichtigung von PID " + IntToStr(PGNotifPid(notif)) + " auf '" + PGNotifChannel(notif) + "': " + PGNotifPayload(notif));
PGFreeNotification(notif);
}
PGUnlisten(conn, "job_queue");
}
==== COPY: Bulk-Import ====
import std.db.postgres;
import std.alloc;
fn BulkLoad(conn: int64): bool {
if (PGCopyBegin(conn, "products", "name,price,stock") == 0) { return false; }
// Jede Zeile als Array von String-Pointern übergeben
var row: int64 := alloc(24); // 3 × 8 Bytes
var c0: pchar := "Widget A";
var c1: pchar := "9.99";
var c2: pchar := "100";
poke64(row, c0 as int64);
poke64(row + 8, c1 as int64);
poke64(row + 16, c2 as int64);
PGCopyRow(conn, row, 3);
free(row, 24);
return PGCopyEnd(conn) == 0;
}
----
===== Hinweise =====
* ''PGConnect'' gibt bei TCP-Fehler 0 zurück, bei Auth-Fehler aber einen gültigen Pointer mit ''PGIsConnected''=0 — deshalb beide Fälle prüfen.
* Das Passwort wird nach dem Handshake im Speicher mit Null überschrieben.
* Parameter-Indizes bei ''PGBind*'' sind **0-basiert** (im Gegensatz zu PostgreSQL-Platzhaltern ''$1'', ''$2'' die 1-basiert sind).
* ''PGQuery'' puffert das komplette Ergebnis im RAM — für sehr große Result-Sets ''PGStmtPrepare'' + ''PGStmtExecute'' verwenden.
* SASL-Authentifizierung (SCRAM-SHA-256) wird nicht unterstützt — für diese Server ''password_encryption = md5'' in der PostgreSQL-Konfiguration setzen.
* Der Connection Pool ist nicht thread-safe — für Multi-Thread-Zugriff pro Thread eine eigene Verbindung verwenden.
----
===== Verwandte Units =====
* ''[[lyx_-_programmiersprache:units:db:sqlite|std.db.sqlite]]'' — SQLite3 (embedded, kein Server)
* ''[[lyx_-_programmiersprache:units:db:mysql|std.db.mysql]]'' — MySQL/MariaDB
* ''[[lyx_-_programmiersprache:units:crypto:md5|std.crypto.md5]]'' — wird für MD5-Authentifizierung verwendet
* ''[[lyx_-_programmiersprache:units:net:socket|std.net.socket]]'' — TCP-Basis
Letzte Aktualisierung: 2026-06-05