TPostgreSQL + FWPG_* Funções

Fonte: source/function/pgsuport.prg

FiveWin provides PostgreSQL integration via the pgsuport.prg module, which bridges the Harbour hbpgsql library (a libpq wrapper) with FWH components such as TXBrowse and TDataRow. Rather than requiring a compile-time link to hbpgsql, the module uses a lazy-loading mechanism to resolve libpq function pointers at runtime, making the library dependency optional.

PGLinked() - Lazy-Loading Function Resolution

The internal static function PGLinked() is called automatically on the first invocation of any FWPG_ function. It uses HB_FUNCPTR() to locate the Harbour-level libpq wrappers provided by the hbpgsql library. If PQEXEC is found, pointers are cached for the following operations:

Pointer Variablehbpgsql FunctionPurpose
pPQExecPQEXECSynchronous command execution
pPQExecParamsPQEXECPARAMSParameterized command execution
pPQMetaDataPQMETADATAResult set schema retrieval
pPQGetValuePQGETVALUEData retrieval for specific row/column
pPQResultStatusPQRESULTSTATUSStatus check (Success / Tuples / Error)
pPQlastrecPQLASTRECRow count retrieval
pPQFCountPQFCOUNTColumn count retrieval
pPQcmdTuplesPQCMDTUPLESAffected rows for DML
pPQresultErrorMessagePQRESULTERRORMESSAGEDetailed server error text

These resolved function pointers are invoked via the HB_ExecFromArray() mechanism through #xtranslate macros (PQ_Exec, PQ_ExecParams, PQ_MetaData, etc.).

FWPG_Execute() - Query Execution

The central function for all PostgreSQL interaction is FWPG_Execute:

Signature:

FWPG_Execute( pDB, cSql, aParams )  && -> aResult | nAffected | .F.

Parameters:

Return values:

When parameters are supplied via aParams, each value is converted to an SQL-safe string using FW_ValToSQL(u, , "PG") and the query is executed via PQ_ExecParams, preventing SQL injection.

PostgreSQL-to-Harbour Type Mapping

Query results arrive from libpq as strings. The internal GetFieldTypes() function inspects column metadata for specific keywords to determine the optimal Harbour type:

Column KeywordHarbour TypeDescription
boolLLogical (converted from 't'/'f')
dateDDate
timestampTTimestamp (date+time)
byteamBinary memo (lowercase m)
textMCharacter memo (uppercase M)
int, numeric, decimalNNumeric
serialNAuto-increment numeric
float, double, realNFloating-point numeric
moneyNCurrency numeric
DefaultCCharacter string

Metadata and Structure Funções

FWPG_MetaStruct()

FWPG_MetaStruct( res )  && -> aStruct

Returns an array of column metadata for a query result. Each element is an array indexed by the following constants:

IndexConstantValueDescription
1_STRU_FIELDNAME1Field (column) name
2_STRU_FIELDTYPE2Harbour type code
3_STRU_FIELDLEN3Field length
4_STRU_FIELDDEC4Decimal places
5_STRU_TABLE5Table OID
6_STRU_TABLECOL6Column ordinal within table
7_STRU_TABLENAME7Source table name
8_STRU_COLNAME8Column name in source table
9_STRU_DEFAULT9Default value expression
10_STRU_PRI10Primary key indicator ("PRI" if key)

FWPG_Structure()

FWPG_Structure( oQry )  && -> aStruct

Returns an enhanced structure array for a PostgreSQL query object. Enriches the base aStruct with table name, column name, and primary key information by querying information_schema.columns. Adjusts field types for bytea (mapped to "m"), timestamp (mapped to "T"), and auto-increment columns (mapped to "+").

FWPG_FieldGet()

FWPG_FieldGet( oQry, nFldNo )  && -> uVal

A specialized field getter that handles PostgreSQL-specific formatting:

FWPG_Skipper()

FWPG_Skipper( oQry, nSkip )  && -> nSkipped

Navigation helper used by TXBrowse codeblocks for record skipping. Calculates the new record offset relative to the current position, clamping to valid range [1, nLastRec]. Returns the actual number of records skipped.

Utility Funções

FunctionDescription
FWPG_ListDbs( oServer )Returns an array of database names on the server (querying pg_database).
FWPG_CurrentDB( oServer )Returns the name of the current database via current_database().
FWPG_ListSchemas( oServer )Returns an array of schema names from information_schema.schemata.
FWPG_ImportFromDBF( oCn, cDbf, cTable, lcAutoInc )Imports a DBF file into a PostgreSQL table. Creates the table structure, auto-maps memo types (binary → bytea, text → TEXT), and bulk-inserts via batching (100 rows by default).
FWPG_CreateTableSQL( cTable, aStruct, lcAutoInc )Generates a CREATE TABLE SQL statement from a DBF-like structure array using SERIAL PRIMARY KEY for auto-increment columns.
FWPG_TableNameFromID( oQry, oID )Resolves a PostgreSQL table OID to a table name via pg_class.
FWPG_PrimaryKeys( oQry, cTable )Returns an array of primary key columns for a given table from information_schema.key_column_usage.

TXBrowse Integration

TXBrowse consumes PostgreSQL data sets through standard codeblock interfaces. The FWPG_Skipper function provides the bSkip codeblock; the query object's bGoTop, bGoBottom, bEof, bBof, and bBookMark codeblocks are set directly on the query object. Direct in-cell editing is supported via FWPG_XBrSaveData( oCol, xValue ), which constructs and executes UPDATE SQL targeting the source table's primary key.

TDataRow Integration

TDataRow provides native PostgreSQL support through two internal methods:

sequenceDiagram participant App as "Application Code" participant DR as "TDataRow" participant PG as "pgsuport.prg" participant Lib as "hbpgsql (libpq)" App->>DR: New( oPostgreQuery ) DR->>PG: FWPG_MetaStruct( res ) PG->>Lib: PQMETADATA DR->>DR: Load() buffer (aData/aOrg) App->>DR: FieldPut( "name", "New Value" ) App->>DR: Save() DR->>PG: FWPG_Execute( pDB, "UPDATE...", {vals} ) PG->>Lib: PQEXECPARAMS

Example: Connect, Query, and Browse

#include "FiveWin.ch"

function Main()

   local oServer, oQry, oWnd, oBrw, aDbs

   // Open a PostgreSQL connection using hbpgsql
   oServer := TPostgreSQL():New( "localhost", "mydb", "postgres", "password", 5432 )

   if oServer:lError
      MsgStop( "Connection failed: " + oServer:cError )
      return nil
   endif

   // List available databases
   aDbs := FWPG_ListDbs( oServer )
   XBROWSER aDbs TITLE "Databases on Server"

   // Execute a query
   oQry := TPostgreSQLQuery():New( oServer, "SELECT id, name, email, city FROM customers ORDER BY name" )

   if oQry:nLastRec > 0
      DEFINE WINDOW oWnd TITLE "Customers"

      @ 0, 0 XBROWSE oBrw OF oWnd ;
         COLUMNS "id", "name", "email", "city" ;
         COLSIZES 50, 200, 250, 150

      oBrw:nMarqueeStyle   := 3
      oBrw:lCanPaste       := .t.

      // Connect XBrowse to PostgreSQL query via codeblocks
      oBrw:bSkip       := { |nSkip| FWPG_Skipper( oQry, nSkip ) }
      oBrw:bGoTop      := { || oQry:GoTop() }
      oBrw:bGoBottom   := { || oQry:GoBottom() }
      oBrw:bEof        := { || oQry:RecNo() > oQry:nLastRec }
      oBrw:bBof        := { || oQry:RecNo() == 0 }
      oBrw:bBookMark   := { || oQry:RecNo() }
      oBrw:bStrData    := { |n| FWPG_FieldGet( oQry, n ) }
      oBrw:bKeyNo      := { || oQry:RecNo() }

      oBrw:CreateFromCode()

      ACTIVATE WINDOW oWnd MAXIMIZED
   else
      MsgInfo( "No records found" )
   endif

   oQry:End()
   oServer:End()

return nil

Notes

Veja Também