pg_test.prg
// ------------------------------------------------------------------
// PostgreSQL Test using Dynamic libpq.dll loading
// ------------------------------------------------------------------
#include "c:\hix\hbdyn.ch"
static pLib
function Main()
local cHost := "localhost"
local cUser := "admin"
local cPass := "1234"
local cDB := "test"
local nPort := 5432
local cConnStr
local pConn, pRes
local nStatus
local cHtml := MemoRead( "c:/hix/pg_test.view" )
local cLogs := ""
local cVectorStatus := ""
local cRawOut := ""
local i, nRows, nCols, cVal
// 1. Load Library
// Try usual locations or current dir
if File( "c:/hix/libpq.dll" )
pLib := hb_LibLoad( "c:/hix/libpq.dll" )
else
pLib := hb_LibLoad( "libpq.dll" )
endif
if Empty( pLib )
cLogs += '<div style="color:red">Error: Could not load libpq.dll</div>'
Render( cHtml, cHost, cDB, cLogs, "Unknown", "DLL Load Failed" )
return nil
endif
cLogs += '<div style="color:green">libpq.dll loaded</div>'
cLogs += '<div style="color:blue">Attempting connection at: ' + Time() + '</div>'
// 2. Connect
// PQconnectdb(const char *conninfo)
cConnStr := "host=" + cHost + " port=" + AllTrim(Str(nPort)) + ;
" dbname=" + cDB + " user=" + cUser + " password=" + cPass + ;
" connect_timeout=10 sslmode=disable"
pConn := PQconnectdb( cConnStr )
if Empty( pConn )
cLogs += '<div style="color:red">PQconnectdb returned NULL (Memory alloc error?)</div>'
Render( cHtml, cHost, cDB, cLogs, "Failed", "" )
// hb_LibFree( pLib )
return ""
endif
// Check Status
// CONNECTION_OK = 0
// CONNECTION_BAD = 1
nStatus := PQstatus( pConn )
if nStatus != 0 // CONNECTION_BAD
cLogs += '<div style="color:red">Connection Failed: ' + PQerrorMessage( pConn ) + '</div>'
PQfinish( pConn )
Render( cHtml, cHost, cDB, cLogs, "Failed", "" )
return ""
endif
cLogs += '<div style="color:green">Connected Successfully!</div>'
// 3. Check for vector extension
pRes := PQexec( pConn, "SELECT * FROM pg_extension WHERE extname = 'vector'" )
if PQresultStatus( pRes ) != 2 // PGRES_TUPLES_OK = 2
cLogs += '<div>Query Error: ' + PQerrorMessage( pConn ) + '</div>'
cVectorStatus := '<span class="status-badge status-warning">Check Failed</span>'
else
nRows := PQntuples( pRes )
if nRows > 0
cVectorStatus := '<span class="status-badge status-success">INSTALLED</span>'
cLogs += '<div>Found pgvector extension!</div>'
// Get details
cVal := PQgetvalue( pRes, 0, 0 ) // Row 0, Col 0 just to see something
// We can enumerate cols slightly better if we knew the schema, usually defaults ok.
else
cVectorStatus := '<span class="status-badge status-error">NOT FOUND</span>'
cLogs += '<div>pgvector extension not found in pg_extension table.</div>'
endif
endif
PQclear( pRes )
// 4. Get PG Version just in case
pRes := PQexec( pConn, "SELECT version()" )
nRows := PQntuples( pRes )
if nRows > 0
cVal := PQgetvalue( pRes, 0, 0 )
cRawOut += "<pre>" + cVal + "</pre>"
endif
PQclear( pRes )
PQfinish( pConn )
// hb_LibFree( pLib ) - Not using explicit free as per mysql example
Render( cHtml, cHost, cDB, cLogs, cVectorStatus, cRawOut )
return ""
function Render( cHtml, cHost, cDB, cLogs, cVector, cRaw )
cHtml := StrTran( cHtml, "{{HOST}}", cHost )
cHtml := StrTran( cHtml, "{{DB}}", cDB )
cHtml := StrTran( cHtml, "{{LOGS}}", cLogs )
cHtml := StrTran( cHtml, "{{VECTOR_STATUS}}", cVector )
cHtml := StrTran( cHtml, "{{RAW_OUTPUT}}", cRaw )
UWrite( cHtml )
return nil
// ------------------------------------------------------------------
// libpq Wrappers
// ------------------------------------------------------------------
function PQconnectdb( cConnInfo )
// PQconnectdb returns PGconn *
return hb_DynCall( { "PQconnectdb", pLib, hb_bitOr( hb_SysLong(),;
hb_SysCallConv() ), HB_DYN_CTYPE_CHAR_PTR }, cConnInfo )
function PQstatus( pConn )
// returns ConnStatusType (enum -> int)
return hb_DynCall( { "PQstatus", pLib, hb_bitOr( HB_DYN_CTYPE_INT,;
hb_SysCallConv() ), hb_SysLong() }, pConn )
function PQerrorMessage( pConn )
// returns char *
return hb_DynCall( { "PQerrorMessage", pLib, hb_bitOr( HB_DYN_CTYPE_CHAR_PTR,;
hb_SysCallConv() ), hb_SysLong() }, pConn )
function PQfinish( pConn )
// void
return hb_DynCall( { "PQfinish", pLib, hb_bitOr( HB_DYN_CTYPE_VOID,;
hb_SysCallConv() ), hb_SysLong() }, pConn )
function PQexec( pConn, cQuery )
// returns PGresult *
return hb_DynCall( { "PQexec", pLib, hb_bitOr( hb_SysLong(),;
hb_SysCallConv() ), hb_SysLong(), HB_DYN_CTYPE_CHAR_PTR }, pConn, cQuery )
function PQresultStatus( pRes )
// returns ExecStatusType (enum -> int)
return hb_DynCall( { "PQresultStatus", pLib, hb_bitOr( HB_DYN_CTYPE_INT,;
hb_SysCallConv() ), hb_SysLong() }, pRes )
function PQntuples( pRes )
// returns int
return hb_DynCall( { "PQntuples", pLib, hb_bitOr( HB_DYN_CTYPE_INT,;
hb_SysCallConv() ), hb_SysLong() }, pRes )
function PQgetvalue( pRes, nRow, nCol )
// returns char *
return hb_DynCall( { "PQgetvalue", pLib, hb_bitOr( HB_DYN_CTYPE_CHAR_PTR,;
hb_SysCallConv() ), hb_SysLong(), HB_DYN_CTYPE_INT, HB_DYN_CTYPE_INT }, pRes, nRow, nCol )
function PQclear( pRes )
// void
return hb_DynCall( { "PQclear", pLib, hb_bitOr( HB_DYN_CTYPE_VOID,;
hb_SysCallConv() ), hb_SysLong() }, pRes )
// Helpers already in hbdyn.ch usage from previous file, but need defining here if not included correctly
// We included hbdyn.ch so constants are there.
// We need these helpers if hb_SysLong/hb_SysCallConv are not built-in (they are usually funcs in my other snippet)
function hb_SysLong()
return HB_DYN_CTYPE_LLONG_UNSIGNED
// On 32bit it's LONG_UNSIGNED, on 64 it's LLONG.
// Assuming 64bit from previous context (libmysql64).
function hb_SysCallConv()
return HB_DYN_CTYPE_DEFAULT // libpq is often CDECL default, not STDCALL.
// Note: windows dlls often use STDCALL, but libpq is cross platform C.
// Let's try CDECL (Default - 0). If it crashes, might need STDCALL.pg_test.view
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PostgreSQL Vector Check</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; background: #f3f4f6; padding: 2rem; color: #1f2937; }
.container { max-width: 800px; margin: 0 auto; background: white; padding: 2rem; border-radius: 1rem; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); }
h1 { color: #336791; border-bottom: 2px solid #e5e7eb; padding-bottom: 1rem; }
.status-badge { padding: 0.25rem 0.75rem; border-radius: 9999px; font-weight: 600; font-size: 0.875rem; }
.status-success { background: #dcfce7; color: #166534; }
.status-error { background: #fee2e2; color: #991b1b; }
.status-warning { background: #fef9c3; color: #854d0e; }
pre { background: #111827; color: #e5e7eb; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; }
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 2rem; }
.info-item { background: #f9fafb; padding: 1rem; border-radius: 0.5rem; }
.info-label { display: block; font-size: 0.875rem; color: #6b7280; margin-bottom: 0.25rem; }
.info-value { font-weight: 600; }
</style>
</head>
<body>
<div class="container">
<h1>PostgreSQL Check</h1>
<div class="info-grid">
<div class="info-item">
<span class="info-label">Server</span>
<span class="info-value">{{HOST}}</span>
</div>
<div class="info-item">
<span class="info-label">Database</span>
<span class="info-value">{{DB}}</span>
</div>
<div class="info-item">
<span class="info-label">Connection Log</span>
<div style="margin-top:0.5rem;">{{LOGS}}</div>
</div>
<div class="info-item">
<span class="info-label">pgvector Extension</span>
<div style="margin-top:0.5rem;">{{VECTOR_STATUS}}</div>
</div>
</div>
<h3>Raw Output</h3>
{{RAW_OUTPUT}}
</div>
</body>
</html>


