Sfoglia il codice sorgente

Initial postgresql module.

mingodad 13 anni fa
parent
commit
83e7d95b2a
7 ha cambiato i file con 913 aggiunte e 1 eliminazioni
  1. 56 0
      ext/dynamic_library.cpp
  2. 16 0
      ext/dynamic_library.h
  3. 737 0
      ext/sq_postgresql.cpp
  4. 72 0
      ext/test-pp.nut
  5. 23 0
      samples/test-postgresql.nut
  6. 2 0
      sq/sq.c
  7. 7 1
      squilu.cbp

+ 56 - 0
ext/dynamic_library.cpp

@@ -0,0 +1,56 @@
+// dynamic_library.cpp
+//
+
+#include "dynamic_library.h"
+#include <stdarg.h>
+
+#ifdef WIN32
+#include <windows.h>
+#define CLOSE_LIB(lib) (FreeLibrary((HINSTANCE)lib) != 0)
+#define OPEN_LIB(lib) (void*)LoadLibrary(lib)
+#define GET_SYM(lib, sn) GetProcAddress((HINSTANCE)lib, sn)
+typedef void* (WINAPI*cPtrFuncVarArg)(...);
+#else
+#include <dlfcn.h>
+#define CLOSE_LIB(lib) (::dlclose(lib) == 0)
+#define OPEN_LIB(lib) ::dlopen(lib,RTLD_LAZY)
+#define GET_SYM(lib, sn) ::dlsym(lib, sn)
+typedef void*(*cPtrFuncVarArg)(...);
+#endif
+
+DynamicLibrary::DynamicLibrary ()
+{
+    handleLib=0;
+}
+
+DynamicLibrary::~ DynamicLibrary ()
+{
+    close();
+}
+
+bool DynamicLibrary::open (char const * libname)
+{
+    //should first close the opened library if any
+    if(handleLib) return false;
+    handleLib=OPEN_LIB(libname);
+    return (handleLib != 0);
+}
+
+bool DynamicLibrary::close ()
+{
+    if(handleLib)
+    {
+        if( CLOSE_LIB(handleLib) )
+        {
+            handleLib = 0;
+            return true;
+        }
+    }
+    return false;
+}
+
+void * DynamicLibrary::dlsym (char const * sym_name)
+{
+    if(handleLib) return (void*)GET_SYM(handleLib,sym_name);
+    else return 0;
+}

+ 16 - 0
ext/dynamic_library.h

@@ -0,0 +1,16 @@
+#ifndef DYNAMIC_LIBRAY_H
+#define DYNAMIC_LIBRAY_H
+
+class DynamicLibrary
+{
+protected:
+  void * handleLib;
+public:
+  DynamicLibrary ();
+  virtual ~ DynamicLibrary ();
+  bool open (char const * libname);
+  bool close ();
+  void * dlsym (char const * sym_name);
+};
+
+#endif //DYNAMIC_LIBRAY_H

+ 737 - 0
ext/sq_postgresql.cpp

@@ -0,0 +1,737 @@
+#include "squirrel.h"
+#include "libpq-fe.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sqstdblobimpl.h"
+
+#include "dynamic_library.h"
+
+/*SquiLu
+local pgsql_functions = [
+    ["ConnStatusType", "PQstatus", "const PGconn *conn"],
+    ["void", "PQfinish", "PGconn *conn"],
+    ["PGresult *", "PQprepare", @"PGconn *conn,
+                        const char *stmtName,
+                        const char *query,
+                        int nParams,
+                        const Oid *paramTypes"],
+    ["PGresult *", "PQdescribePrepared", "PGconn *conn, const char *stmtName"],
+    ["int", "PQnparams", "const PGresult *res"],
+    ["Oid", "PQparamtype", "const PGresult *res, int param_number"],
+    ["PGresult *", "PQexecPrepared", @"PGconn *conn,
+                         const char *stmtName,
+                         int nParams,
+                         const char * const *paramValues,
+                         const int *paramLengths,
+                         const int *paramFormats,
+                         int resultFormat"],
+    ["PGresult *", "PQexec", "PGconn *conn, const char *command"],
+    ["const char*", "PQgetvalue", @"const PGresult *res,
+                         int row_number,
+                         int column_number"],
+    ["int", "PQntuples", "const PGresult *res"],
+    ["char *", "PQcmdTuples", "const PGresult *res"],
+    ["int", "PQnfields", "const PGresult *res"],
+    ["int", "PQgetisnull", @"const PGresult *res,
+                        int row_number,
+                        int column_number"],
+    ["void", "PQclear", "const PGresult *res"],
+    ["int", "PQfnumber", "const PGresult *res, const char *column_name"],
+    ["char *", "PQfname", "const PGresult *res, int column_number"],
+    ["ExecStatusType", "PQresultStatus", "const PGresult *res"],
+    ["char *", "PQerrorMessage", "const PGconn *conn"],
+    ["void", "PQreset", "PGconn *conn"],
+    ["PGresult *", "PQgetResult", "PGconn *conn"],
+    ["int", "PQsetnonblocking", "PGconn *conn, int arg"],
+    ["Oid", "PQftype", "const PGresult *res, int column_number"],
+    ["int", "PQserverVersion", "const PGconn *conn"],
+
+    // Large-object access routines
+    ["int", "lo_open", "PGconn *conn, Oid lobjId, int mode"],
+    ["int", "lo_close", "PGconn *conn, int fd"],
+    ["int", "lo_read", "PGconn *conn, int fd, char *buf, size_t len"],
+    ["int", "lo_write", "PGconn *conn, int fd, const char *buf, size_t len"],
+    ["int", "lo_lseek", "PGconn *conn, int fd, int offset, int whence"],
+    ["Oid",	"lo_creat", "PGconn *conn, int mode"],
+    ["Oid",	"lo_create", "PGconn *conn, Oid lobjId"],
+    ["int", "lo_tell", "PGconn *conn, int fd"],
+    ["int", "lo_truncate", "PGconn *conn, int fd, size_t len"],
+    ["int", "lo_unlink", "PGconn *conn, Oid lobjId"],
+    ["Oid",	"lo_import", "PGconn *conn, const char *filename"],
+    ["Oid",	"lo_import_with_oid", "PGconn *conn, const char *filename, Oid lobjId"],
+    ["int", "lo_export", "PGconn *conn, Oid lobjId, const char *filename"],
+
+    //next entry should be the last one
+    //to make valid the test made on load_libpq function
+    ["PGconn *", "PQconnectdb", "const char *conninfo"],
+];
+
+function write_pgsql_functions_declaration(){
+    foreach(k,v in pgsql_functions) {
+        putsnl("typedef " + v[0] + " (*" + v[1] + "_t)(" + v[2] + ");");
+        putsnl("static " + v[1] + "_t dl" + v[1] + " = 0;");
+    }
+}
+
+function write_pgsql_functions_load(){
+    foreach(k,v in pgsql_functions){
+        putsnl("dl" + v[1] + " = (" + v[1] + "_t) libpq.dlsym(\"" + v[1] + "\");");
+        putsnl("if(!dl" + v[1] + ") return false;");
+    }
+}
+SquiLu*/
+
+static DynamicLibrary libpq;
+
+//@write_pgsql_functions_declaration();
+// generated-code:begin
+typedef ConnStatusType (*PQstatus_t)(const PGconn *conn);
+static PQstatus_t dlPQstatus = 0;
+typedef void (*PQfinish_t)(PGconn *conn);
+static PQfinish_t dlPQfinish = 0;
+typedef PGresult * (*PQprepare_t)(PGconn *conn,
+                        const char *stmtName,
+                        const char *query,
+                        int nParams,
+                        const Oid *paramTypes);
+static PQprepare_t dlPQprepare = 0;
+typedef PGresult * (*PQdescribePrepared_t)(PGconn *conn, const char *stmtName);
+static PQdescribePrepared_t dlPQdescribePrepared = 0;
+typedef int (*PQnparams_t)(const PGresult *res);
+static PQnparams_t dlPQnparams = 0;
+typedef Oid (*PQparamtype_t)(const PGresult *res, int param_number);
+static PQparamtype_t dlPQparamtype = 0;
+typedef PGresult * (*PQexecPrepared_t)(PGconn *conn,
+                         const char *stmtName,
+                         int nParams,
+                         const char * const *paramValues,
+                         const int *paramLengths,
+                         const int *paramFormats,
+                         int resultFormat);
+static PQexecPrepared_t dlPQexecPrepared = 0;
+typedef PGresult * (*PQexec_t)(PGconn *conn, const char *command);
+static PQexec_t dlPQexec = 0;
+typedef const char* (*PQgetvalue_t)(const PGresult *res,
+                         int row_number,
+                         int column_number);
+static PQgetvalue_t dlPQgetvalue = 0;
+typedef int (*PQntuples_t)(const PGresult *res);
+static PQntuples_t dlPQntuples = 0;
+typedef char * (*PQcmdTuples_t)(const PGresult *res);
+static PQcmdTuples_t dlPQcmdTuples = 0;
+typedef int (*PQnfields_t)(const PGresult *res);
+static PQnfields_t dlPQnfields = 0;
+typedef int (*PQgetisnull_t)(const PGresult *res,
+                        int row_number,
+                        int column_number);
+static PQgetisnull_t dlPQgetisnull = 0;
+typedef void (*PQclear_t)(const PGresult *res);
+static PQclear_t dlPQclear = 0;
+typedef int (*PQfnumber_t)(const PGresult *res, const char *column_name);
+static PQfnumber_t dlPQfnumber = 0;
+typedef char * (*PQfname_t)(const PGresult *res, int column_number);
+static PQfname_t dlPQfname = 0;
+typedef ExecStatusType (*PQresultStatus_t)(const PGresult *res);
+static PQresultStatus_t dlPQresultStatus = 0;
+typedef char * (*PQerrorMessage_t)(const PGconn *conn);
+static PQerrorMessage_t dlPQerrorMessage = 0;
+typedef void (*PQreset_t)(PGconn *conn);
+static PQreset_t dlPQreset = 0;
+typedef PGresult * (*PQgetResult_t)(PGconn *conn);
+static PQgetResult_t dlPQgetResult = 0;
+typedef int (*PQsetnonblocking_t)(PGconn *conn, int arg);
+static PQsetnonblocking_t dlPQsetnonblocking = 0;
+typedef Oid (*PQftype_t)(const PGresult *res, int column_number);
+static PQftype_t dlPQftype = 0;
+typedef int (*PQserverVersion_t)(const PGconn *conn);
+static PQserverVersion_t dlPQserverVersion = 0;
+typedef int (*lo_open_t)(PGconn *conn, Oid lobjId, int mode);
+static lo_open_t dllo_open = 0;
+typedef int (*lo_close_t)(PGconn *conn, int fd);
+static lo_close_t dllo_close = 0;
+typedef int (*lo_read_t)(PGconn *conn, int fd, char *buf, size_t len);
+static lo_read_t dllo_read = 0;
+typedef int (*lo_write_t)(PGconn *conn, int fd, const char *buf, size_t len);
+static lo_write_t dllo_write = 0;
+typedef int (*lo_lseek_t)(PGconn *conn, int fd, int offset, int whence);
+static lo_lseek_t dllo_lseek = 0;
+typedef Oid (*lo_creat_t)(PGconn *conn, int mode);
+static lo_creat_t dllo_creat = 0;
+typedef Oid (*lo_create_t)(PGconn *conn, Oid lobjId);
+static lo_create_t dllo_create = 0;
+typedef int (*lo_tell_t)(PGconn *conn, int fd);
+static lo_tell_t dllo_tell = 0;
+typedef int (*lo_truncate_t)(PGconn *conn, int fd, size_t len);
+static lo_truncate_t dllo_truncate = 0;
+typedef int (*lo_unlink_t)(PGconn *conn, Oid lobjId);
+static lo_unlink_t dllo_unlink = 0;
+typedef Oid (*lo_import_t)(PGconn *conn, const char *filename);
+static lo_import_t dllo_import = 0;
+typedef Oid (*lo_import_with_oid_t)(PGconn *conn, const char *filename, Oid lobjId);
+static lo_import_with_oid_t dllo_import_with_oid = 0;
+typedef int (*lo_export_t)(PGconn *conn, Oid lobjId, const char *filename);
+static lo_export_t dllo_export = 0;
+typedef PGconn * (*PQconnectdb_t)(const char *conninfo);
+static PQconnectdb_t dlPQconnectdb = 0;
+// generated-code:end
+#ifdef WIN32
+#define LIBPQ_NAME "libpq.dll"
+#else
+#define LIBPQ_NAME "libpq.so"
+#endif
+
+#define DONT_DELETE_MSG 1
+
+static bool load_libpq()
+{
+    if(dlPQconnectdb) return true;
+    if(libpq.open(LIBPQ_NAME))
+    {
+        //@write_pgsql_functions_load();
+// generated-code:begin
+dlPQstatus = (PQstatus_t) libpq.dlsym("PQstatus");
+if(!dlPQstatus) return false;
+dlPQfinish = (PQfinish_t) libpq.dlsym("PQfinish");
+if(!dlPQfinish) return false;
+dlPQprepare = (PQprepare_t) libpq.dlsym("PQprepare");
+if(!dlPQprepare) return false;
+dlPQdescribePrepared = (PQdescribePrepared_t) libpq.dlsym("PQdescribePrepared");
+if(!dlPQdescribePrepared) return false;
+dlPQnparams = (PQnparams_t) libpq.dlsym("PQnparams");
+if(!dlPQnparams) return false;
+dlPQparamtype = (PQparamtype_t) libpq.dlsym("PQparamtype");
+if(!dlPQparamtype) return false;
+dlPQexecPrepared = (PQexecPrepared_t) libpq.dlsym("PQexecPrepared");
+if(!dlPQexecPrepared) return false;
+dlPQexec = (PQexec_t) libpq.dlsym("PQexec");
+if(!dlPQexec) return false;
+dlPQgetvalue = (PQgetvalue_t) libpq.dlsym("PQgetvalue");
+if(!dlPQgetvalue) return false;
+dlPQntuples = (PQntuples_t) libpq.dlsym("PQntuples");
+if(!dlPQntuples) return false;
+dlPQcmdTuples = (PQcmdTuples_t) libpq.dlsym("PQcmdTuples");
+if(!dlPQcmdTuples) return false;
+dlPQnfields = (PQnfields_t) libpq.dlsym("PQnfields");
+if(!dlPQnfields) return false;
+dlPQgetisnull = (PQgetisnull_t) libpq.dlsym("PQgetisnull");
+if(!dlPQgetisnull) return false;
+dlPQclear = (PQclear_t) libpq.dlsym("PQclear");
+if(!dlPQclear) return false;
+dlPQfnumber = (PQfnumber_t) libpq.dlsym("PQfnumber");
+if(!dlPQfnumber) return false;
+dlPQfname = (PQfname_t) libpq.dlsym("PQfname");
+if(!dlPQfname) return false;
+dlPQresultStatus = (PQresultStatus_t) libpq.dlsym("PQresultStatus");
+if(!dlPQresultStatus) return false;
+dlPQerrorMessage = (PQerrorMessage_t) libpq.dlsym("PQerrorMessage");
+if(!dlPQerrorMessage) return false;
+dlPQreset = (PQreset_t) libpq.dlsym("PQreset");
+if(!dlPQreset) return false;
+dlPQgetResult = (PQgetResult_t) libpq.dlsym("PQgetResult");
+if(!dlPQgetResult) return false;
+dlPQsetnonblocking = (PQsetnonblocking_t) libpq.dlsym("PQsetnonblocking");
+if(!dlPQsetnonblocking) return false;
+dlPQftype = (PQftype_t) libpq.dlsym("PQftype");
+if(!dlPQftype) return false;
+dlPQserverVersion = (PQserverVersion_t) libpq.dlsym("PQserverVersion");
+if(!dlPQserverVersion) return false;
+dllo_open = (lo_open_t) libpq.dlsym("lo_open");
+if(!dllo_open) return false;
+dllo_close = (lo_close_t) libpq.dlsym("lo_close");
+if(!dllo_close) return false;
+dllo_read = (lo_read_t) libpq.dlsym("lo_read");
+if(!dllo_read) return false;
+dllo_write = (lo_write_t) libpq.dlsym("lo_write");
+if(!dllo_write) return false;
+dllo_lseek = (lo_lseek_t) libpq.dlsym("lo_lseek");
+if(!dllo_lseek) return false;
+dllo_creat = (lo_creat_t) libpq.dlsym("lo_creat");
+if(!dllo_creat) return false;
+dllo_create = (lo_create_t) libpq.dlsym("lo_create");
+if(!dllo_create) return false;
+dllo_tell = (lo_tell_t) libpq.dlsym("lo_tell");
+if(!dllo_tell) return false;
+dllo_truncate = (lo_truncate_t) libpq.dlsym("lo_truncate");
+if(!dllo_truncate) return false;
+dllo_unlink = (lo_unlink_t) libpq.dlsym("lo_unlink");
+if(!dllo_unlink) return false;
+dllo_import = (lo_import_t) libpq.dlsym("lo_import");
+if(!dllo_import) return false;
+dllo_import_with_oid = (lo_import_with_oid_t) libpq.dlsym("lo_import_with_oid");
+if(!dllo_import_with_oid) return false;
+dllo_export = (lo_export_t) libpq.dlsym("lo_export");
+if(!dllo_export) return false;
+dlPQconnectdb = (PQconnectdb_t) libpq.dlsym("PQconnectdb");
+if(!dlPQconnectdb) return false;
+// generated-code:end
+
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const SQChar *PostgreSQL_TAG = _SC("PostgreSQL");
+
+static SQRESULT get_pgsql_instance(HSQUIRRELVM v, SQInteger idx, PGconn **self){
+    SQRESULT _rc_;
+	if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)PostgreSQL_TAG)) < 0) return _rc_;
+	if(!*self) return sq_throwerror(v, _SC("database is closed"));
+	return _rc_;
+}
+
+#define GET_pgsql_INSTANCE_AT(idx) \
+	PGconn *self=NULL; \
+	if((_rc_ = get_pgsql_instance(v,idx,&self)) < 0) return _rc_;
+
+#define GET_pgsql_INSTANCE() GET_pgsql_INSTANCE_AT(1)
+
+static const SQChar *PostgreSQL_Result_TAG = _SC("PostgreSQL_Result");
+static const SQChar *_curr_row_key = _SC("_curr_row");
+
+static SQRESULT get_pgsql_result_instance(HSQUIRRELVM v, SQInteger idx, PGresult **self){
+    SQRESULT _rc_;
+	if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)PostgreSQL_Result_TAG)) < 0) return _rc_;
+	if(!*self) return sq_throwerror(v, _SC("PGresult is closed"));
+	return _rc_;
+}
+
+#define GET_pgsql_result_INSTANCE_AT(idx) \
+	PGresult *self=NULL; \
+	if((_rc_ = get_pgsql_result_instance(v,idx,&self)) < 0) return _rc_;
+
+#define GET_pgsql_result_INSTANCE() GET_pgsql_result_INSTANCE_AT(1)
+
+static SQRESULT sq_pgsql_result_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v)
+{
+	PGresult *self = ((PGresult *)p);
+	if (self) dlPQclear(self);
+	return 0;
+}
+
+static SQRESULT sq_pgsql_result_close(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	dlPQclear(self);
+    sq_setinstanceup(v, 1, 0); //next calls will fail with "Pgresult is closed"
+	return 0;
+}
+
+static SQRESULT sq_pgsql_result_col_count(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	sq_pushinteger(v, dlPQnfields(self));
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_row_count(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	sq_pushinteger(v, dlPQntuples(self));
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_col_name(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	SQ_GET_INTEGER(v, 2, col);
+	sq_pushstring(v, dlPQfname(self, col), -1);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_col_index(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	SQ_GET_STRING(v, 2, name);
+	sq_pushinteger(v, dlPQfnumber(self, name));
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_eof(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+    sq_pushstring(v, _curr_row_key, -1);
+    if(sq_get(v, 1) == SQ_OK){
+        SQ_GET_INTEGER(v, -1, curr_row);
+        sq_pushbool(v, curr_row < dlPQntuples(self));
+    }
+	else sq_pushbool(v, SQTrue);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_next_row(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+    sq_pushstring(v, _curr_row_key, -1);
+    sq_push(v, -1); //make a copy
+    if(sq_get(v, 1) == SQ_OK){
+        SQ_GET_INTEGER(v, -1, curr_row);
+        if(++curr_row < dlPQntuples(self)){
+            sq_poptop(v);
+            sq_pushinteger(v, curr_row);
+            sq_set(v, 1);
+            sq_pushbool(v, SQTrue);
+            return 1;
+        }
+    }
+	sq_pushbool(v, SQFalse);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_result_col_value(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_result_INSTANCE();
+	SQObjectType ptype = sq_gettype(v, 2);
+	int col = -1;
+	if(ptype == OT_STRING){
+	    SQ_GET_STRING(v, 2, col_name);
+	    col = dlPQfnumber(self, col_name);
+	}
+	else
+	{
+	    SQ_GET_INTEGER(v, 2, idx);
+	    col = idx;
+	}
+	if(col < 0) return sq_throwerror(v, _SC("invalid col index/name"));
+
+    sq_pushstring(v, _curr_row_key, -1);
+    if(sq_get(v, 1) == SQ_OK){
+        SQ_GET_INTEGER(v, -1, curr_row);
+        if(curr_row < dlPQntuples(self)){
+            sq_pushstring(v, dlPQgetvalue(self, curr_row, col), -1);
+            return 1;
+        }
+    }
+	return SQ_ERROR;
+}
+
+static SQRESULT sq_pgsql_result_row_as_array(HSQUIRRELVM v){
+	SQ_FUNC_VARS(v);
+	GET_pgsql_result_INSTANCE();
+	SQ_OPT_INTEGER(v, 2, row, -1);
+    if(row < 0){
+        sq_pushstring(v, _curr_row_key, -1);
+        if(sq_get(v, 1) == SQ_OK){
+            sq_getinteger(v, -1, &row);
+        }
+    }
+    int row_count = dlPQntuples(self);
+    if(row < 0 || row >= row_count) return sq_throwerror(v, _SC("invalid row (%d)"), row);
+
+    int col_count = dlPQnfields(self);
+    sq_newarray(v, col_count);
+    for(int i=0; i < col_count; ++i){
+        sq_pushinteger(v, i);
+        sq_pushstring(v, dlPQgetvalue(self, row, i), -1);
+        sq_rawset(v, -3);
+    }
+	return 1;
+}
+
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),  sq_pgsql_result_##name,nparams,tycheck}
+static SQRegFunction sq_pgsql_result_methods[] =
+{
+	_DECL_FUNC(close,  1, _SC("x")),
+	_DECL_FUNC(eof,  1, _SC("x")),
+	_DECL_FUNC(next_row,  1, _SC("x")),
+	_DECL_FUNC(col_count,  1, _SC("x")),
+	_DECL_FUNC(row_count,  1, _SC("x")),
+	_DECL_FUNC(col_name,  2, _SC("xi")),
+	_DECL_FUNC(col_index,  2, _SC("xs")),
+	_DECL_FUNC(col_value,  2, _SC("x i|s")),
+	_DECL_FUNC(row_as_array,  -1, _SC("xi")),
+	{0,0}
+};
+#undef _DECL_FUNC
+
+static SQRESULT sq_pgsql_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v)
+{
+	PGconn *self = ((PGconn *)p);
+	if (self) dlPQfinish(self);
+	return 0;
+}
+
+static SQRESULT sq_pgsql_constructor(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    SQ_GET_STRING(v, 2, szConnInfo);
+    PGconn *self=0;
+
+    if(load_libpq())
+    {
+        self = dlPQconnectdb(szConnInfo);
+        if (dlPQstatus(self) == CONNECTION_BAD) return sq_throwerror(v, _SC("Failed to connect ot database !"));
+    }
+    else return sq_throwerror(v, _SC("Failed to load libpq !"));
+
+    sq_setinstanceup(v, 1, self);
+    sq_setreleasehook(v,1, sq_pgsql_releasehook);
+
+    //save a weakref to allow statement return it's db
+    sq_pushuserpointer(v, self);
+    sq_weakref(v, 1);
+    sq_setonregistrytable(v);
+
+    return 1;
+}
+
+static SQRESULT sq_pgsql_close(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+	dlPQfinish(self);
+    sq_setinstanceup(v, 1, 0); //next calls will fail with "database is closed"
+	return 0;
+}
+
+static SQRESULT sq_pgsql_exec_dml(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+    SQ_GET_STRING(v, 2, szSQL);
+     int result = 0;
+
+    PGresult *qres = dlPQexec(self, szSQL);
+
+    bool is_ok = dlPQresultStatus(qres) == PGRES_COMMAND_OK;
+
+    if (is_ok) result = atoi(dlPQcmdTuples(qres));
+    dlPQclear(qres);
+
+    if (!is_ok) return sq_throwerror(v, dlPQerrorMessage(self));
+
+    sq_pushinteger(v, result);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_exec_scalar(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+    SQ_GET_STRING(v, 2, szSQL);
+     int result = 0;
+
+    PGresult *qres = dlPQexec(self, szSQL);
+
+    bool is_ok = (dlPQresultStatus(qres) == PGRES_TUPLES_OK) &&
+                 (dlPQntuples(qres) == 1) && (dlPQnfields(qres) > 0);
+
+    if (is_ok) result = atoi(dlPQgetvalue(qres, 0, 0));
+    dlPQclear(qres);
+
+    if (!is_ok) return sq_throwerror(v, dlPQerrorMessage(self));
+
+    sq_pushinteger(v, result);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_exec_query(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+    SQ_GET_STRING(v, 2, szSQL);
+
+    PGresult *qres = dlPQexec(self, szSQL);
+
+    if(dlPQresultStatus(qres) == PGRES_TUPLES_OK){
+        sq_pushroottable(v);
+        sq_pushstring(v, PostgreSQL_Result_TAG, -1);
+        if(sq_get(v, -2) == SQ_OK){
+            if(sq_createinstance(v, -1) == SQ_OK){
+                sq_setinstanceup(v, -1, qres);
+                sq_setreleasehook(v, -1, sq_pgsql_result_releasehook);
+                sq_pushstring(v, _curr_row_key, -1);
+                sq_pushinteger(v, -1);
+                sq_set(v, -3);
+                return 1;
+            }
+        }
+    }
+    return sq_throwerror(v, dlPQerrorMessage(self));
+}
+
+static SQRESULT sq_pgsql_error_message(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+	sq_pushstring(v, dlPQerrorMessage(self), -1);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_version(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+	sq_pushinteger(v, dlPQserverVersion(self));
+	return 1;
+}
+
+static int
+ inv_read =  0x40000,
+ inv_write = 0x20000,
+ invalidoid = 0,
+ inv_seek_set = 0,
+ inv_seek_curr = 1,
+ inv_seek_end = 2;
+
+static SQRESULT sq_pgsql_get_blob_field(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+	SQ_GET_INTEGER(v, 2, oid);
+    //begin_recursive_transaction();
+    char *result = 0;
+	int ofd = dllo_open(self, oid, inv_read);
+	if(ofd >= 0){
+		int blobSize = dllo_lseek(self, ofd, 0, inv_seek_end);
+		dllo_lseek(self, ofd, 0, inv_seek_set);
+	    SQBlob blob(blobSize);
+		result = (char*)blob.GetBuf();
+		int numRead = 0;
+		while(blobSize > 0){
+		 	int i = dllo_read(self, ofd, result+numRead, blobSize);
+			numRead += i;
+			blobSize -= i;
+		}
+		dllo_close(self, oid);
+		sq_pushstring(v, (const SQChar*)blob.GetBuf(), blob.Len());
+	}
+	//commit_recursive_transaction();
+	if(!result) sq_pushnull(v);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_insert_blob_field(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+    int result = 0;
+    SQ_GET_STRING(v, 2, blob);
+    SQ_GET_BOOL(v, 3, isFileName);
+	if(isFileName){
+		result = dllo_import(self, blob);
+	} else {
+		result = dllo_creat(self, inv_write);
+		if(result){
+			int ofd = dllo_open(self, result, inv_write);
+			if (ofd >= 0){
+				int i = blob_size;
+				const char *blopPtr = (const char *)blob;
+				int numWriten = 0;
+				while(i > 0){
+				 	int i2 = dllo_write(self, ofd, blopPtr+numWriten, i);
+					numWriten += i2;
+					i -= i2;
+				}
+				dllo_close(self, ofd);
+			}
+			else return sq_throwerror(v, _SC("Failed to insert blob !"));
+		}
+	}
+	if(!result) sq_pushnull(v);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_update_blob_field(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+    SQ_GET_INTEGER(v, 2, oid);
+    SQ_GET_STRING(v, 3, blob);
+    SQ_GET_BOOL(v, 4, isFileName);
+
+    int result_oid = 0;
+    int result_error = SQ_OK;
+	int loid = dllo_creat(self, inv_write);
+	int ofd = dllo_open(self, loid, inv_write);
+	if(ofd >= 0){
+		dllo_unlink(self, oid);
+		result_oid = loid;
+		if(isFileName)
+		{
+			char buf[2048];
+			FILE *fp = fopen(blob, "rb");
+			if(!fp) {
+			    sq_throwerror(v, _SC("Failed to update blob from file !"));
+			    result_error = SQ_ERROR;
+			}
+			else
+			{
+                char *charPtr = buf;
+                int numRead;
+                do{
+                    numRead = fread(buf, 1, sizeof(buf), fp);
+                    int numWriten = dllo_write(self, ofd, charPtr, numRead);
+                    if (numWriten != numRead) {
+                        sq_throwerror(v, _SC("Failed to update blob from file !"));
+                        result_error = SQ_ERROR;
+                        break;
+                    }
+                } while (numRead == 0);
+                fclose(fp);
+			}
+		}else{
+			int i = blob_size;
+            const char *blopPtr = (const char *)blob;
+			int numWriten = 0;
+			while(i > 0){
+			 	int i2 = dllo_write(self, ofd, blopPtr+numWriten, i);
+				numWriten += i2;
+				i -= i2;
+			}
+		}
+		dllo_close(self, ofd);
+	}
+	if(result_error == SQ_ERROR) return result_error;
+	sq_pushinteger(v, result_oid);
+	return 1;
+}
+
+static SQRESULT sq_pgsql_delete_blob_field(HSQUIRRELVM v){
+	SQ_FUNC_VARS_NO_TOP(v);
+	GET_pgsql_INSTANCE();
+	SQ_GET_INTEGER(v, 2, oid);
+	sq_pushinteger(v, dllo_unlink(self, oid));
+	return 1;
+}
+
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),  sq_pgsql_##name,nparams,tycheck}
+static SQRegFunction sq_pgsql_methods[] =
+{
+	_DECL_FUNC(constructor,  2, _SC("xs")),
+	_DECL_FUNC(close,  1, _SC("x")),
+	_DECL_FUNC(exec_dml,  2, _SC("xs")),
+	_DECL_FUNC(exec_scalar,  2, _SC("xs")),
+	_DECL_FUNC(exec_query,  2, _SC("xs")),
+	_DECL_FUNC(error_message,  1, _SC("x")),
+	_DECL_FUNC(version,  1, _SC("x")),
+	_DECL_FUNC(get_blob_field,  2, _SC("xi")),
+	_DECL_FUNC(insert_blob_field,  3, _SC("xsb")),
+	_DECL_FUNC(update_blob_field,  3, _SC("xisb")),
+	_DECL_FUNC(delete_blob_field,  2, _SC("xi")),
+	{0,0}
+};
+#undef _DECL_FUNC
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQRESULT sqext_register_PostgreSQL(HSQUIRRELVM v)
+{
+    sq_pushstring(v,PostgreSQL_TAG,-1);
+    sq_newclass(v,SQFalse);
+    sq_settypetag(v,-1,(void*)PostgreSQL_TAG);
+    sq_insert_reg_funcs(v, sq_pgsql_methods);
+    sq_newslot(v,-3,SQTrue);
+
+    sq_pushstring(v,PostgreSQL_Result_TAG,-1);
+    sq_newclass(v,SQFalse);
+    sq_settypetag(v,-1,(void*)PostgreSQL_Result_TAG);
+    sq_insert_reg_funcs(v, sq_pgsql_result_methods);
+    sq_pushstring(v, _curr_row_key, -1);
+    sq_pushnull(v);
+    sq_newslot(v, -3, SQFalse);
+    sq_newslot(v,-3,SQTrue);
+
+    return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+

+ 72 - 0
ext/test-pp.nut

@@ -0,0 +1,72 @@
+local pgsql_functions = [
+    ["ConnStatusType", "PQstatus", "const PGconn *conn"],
+    ["void", "PQfinish", "PGconn *conn"],
+    ["PGresult *", "PQprepare", @"PGconn *conn,
+                        const char *stmtName,
+                        const char *query,
+                        int nParams,
+                        const Oid *paramTypes"],
+    ["PGresult *", "PQdescribePrepared", "PGconn *conn, const char *stmtName"],
+    ["int", "PQnparams", "const PGresult *res"],
+    ["Oid", "PQparamtype", "const PGresult *res, int param_number"],
+    ["PGresult *", "PQexecPrepared", @"PGconn *conn,
+                         const char *stmtName,
+                         int nParams,
+                         const char * const *paramValues,
+                         const int *paramLengths,
+                         const int *paramFormats,
+                         int resultFormat"],
+    ["PGresult *", "PQexec", "PGconn *conn, const char *command"],
+    ["const char*", "PQgetvalue", @"const PGresult *res,
+                         int row_number,
+                         int column_number"],
+    ["int", "PQntuples", "const PGresult *res"],
+    ["char *", "PQcmdTuples", "const PGresult *res"],
+    ["int", "PQnfields", "const PGresult *res"],
+    ["int", "PQgetisnull", @"const PGresult *res,
+                        int row_number,
+                        int column_number"],
+    ["void", "PQclear", "const PGresult *res"],
+    ["int", "PQfnumber", "const PGresult *res, const char *column_name"],
+    ["char *", "PQfname", "const PGresult *res, int column_number"],
+    ["ExecStatusType", "PQresultStatus", "const PGresult *res"],
+    ["char *", "PQerrorMessage", "const PGconn *conn"],
+    ["void", "PQreset", "PGconn *conn"],
+    ["PGresult *", "PQgetResult", "PGconn *conn"],
+    ["int", "PQsetnonblocking", "PGconn *conn, int arg"],
+    ["Oid", "PQftype", "const PGresult *res, int column_number"],
+    ["int", "PQserverVersion", "const PGconn *conn"],
+
+    // Large-object access routines
+    ["int", "lo_open", "PGconn *conn, Oid lobjId, int mode"],
+    ["int", "lo_close", "PGconn *conn, int fd"],
+    ["int", "lo_read", "PGconn *conn, int fd, char *buf, size_t len"],
+    ["int", "lo_write", "PGconn *conn, int fd, const char *buf, size_t len"],
+    ["int", "lo_lseek", "PGconn *conn, int fd, int offset, int whence"],
+    ["Oid",	"lo_creat", "PGconn *conn, int mode"],
+    ["Oid",	"lo_create", "PGconn *conn, Oid lobjId"],
+    ["int", "lo_tell", "PGconn *conn, int fd"],
+    ["int", "lo_truncate", "PGconn *conn, int fd, size_t len"],
+    ["int", "lo_unlink", "PGconn *conn, Oid lobjId"],
+    ["Oid",	"lo_import", "PGconn *conn, const char *filename"],
+    ["Oid",	"lo_import_with_oid", "PGconn *conn, const char *filename, Oid lobjId"],
+    ["int", "lo_export", "PGconn *conn, Oid lobjId, const char *filename"],
+
+    //next entry should be the last one
+    //to make valid the test made on load_libpq function
+    ["PGconn *", "PQconnectdb", "const char *conninfo"],
+];
+
+function write_pgsql_functions_declaration(){
+    foreach(k,v in pgsql_functions) {
+        putsnl("typedef " + v[1] + " (*" + v[2] + "_t)(" + v[3] + ");");
+        putsnl("static " + v[2] + "_t dl" + v[2] + " = 0;");
+    }
+}
+
+function write_pgsql_functions_load(){
+    foreach(k,v in pgsql_functions){
+        putsnl("dl" + v[2] + " = (" + v[2] + "_t) libpq.dlsym(\"" + v[2] + "\");");
+        putsnl("if(!dl" + v[2] + ") return false;");
+    }
+}

+ 23 - 0
samples/test-postgresql.nut

@@ -0,0 +1,23 @@
+local db = PostgreSQL("host=localhost dbname=ourbiz user=ourbiz password=ourbiz2");
+print(db.version());
+print(db.exec_scalar("select count(*) from pg_type;"));
+local qry = db.exec_query("select * from pg_type;");
+local col_count = qry.col_count();
+while(qry.next_row()){
+	for(local i=0; i < col_count; ++i){
+		print1(qry.col_value(i));
+		print1("|");
+	}
+	print1("\n");
+}
+
+qry._curr_row = -1;
+while(qry.next_row()){
+	foreach(v in qry.row_as_array()){
+		print1(v);
+		print1("|");
+	}
+	print1("\n");
+}
+
+db.close();

+ 2 - 0
sq/sq.c

@@ -450,6 +450,7 @@ SQRESULT sqext_register_rs232(HSQUIRRELVM v);
 SQRESULT sqext_register_tinyxml2(HSQUIRRELVM v);
 SQRESULT sqext_register_decimal(HSQUIRRELVM v);
 SQRESULT sqext_register_markdown(HSQUIRRELVM v);
+SQRESULT sqext_register_PostgreSQL(HSQUIRRELVM v);
 
 int main(int argc, char* argv[])
 {
@@ -478,6 +479,7 @@ int main(int argc, char* argv[])
 	sqext_register_base64(v);
 	sqext_register_Sq_Fpdf(v);
 	sqext_register_SQLite3(v);
+	sqext_register_PostgreSQL(v);
 	//sqext_register_dad_utils(v);
 	sqext_register_mix(v);
 	sqext_register_sqfs(v);

+ 7 - 1
squilu.cbp

@@ -11,7 +11,7 @@
 				<Option object_output="obj/Debug/" />
 				<Option type="1" />
 				<Option compiler="gcc" />
-				<Option parameters="ourbiz-client.nut" />
+				<Option parameters="test-weakref.nut" />
 				<Compiler>
 					<Add option="-g" />
 					<Add directory="../zeromq-3.2.2/include" />
@@ -134,6 +134,7 @@
 				<Option object_output="obj/Debug/" />
 				<Option type="1" />
 				<Option compiler="gcc" />
+				<Option parameters="test.nut" />
 				<Compiler>
 					<Add option="-Wall" />
 					<Add option="-g" />
@@ -172,6 +173,7 @@
 		<Compiler>
 			<Add option="-Wall" />
 			<Add option="-fno-strict-aliasing" />
+			<Add option="-D_DEBUG_DUMP2=1" />
 			<Add option="-DWITH_DAD_EXTRAS=1" />
 			<Add option="-DSQ_SUBLATIN=1" />
 			<Add option="-DNEED_SUBLATIN_C2=1" />
@@ -209,6 +211,7 @@
 			<Add directory="ext/myaxtls" />
 			<Add directory="../dadbiz++/third-party/sqlite3" />
 			<Add directory="../dadbiz++/third-party/discount-2.1.2" />
+			<Add directory="/usr/include/postgresql" />
 		</Compiler>
 		<Linker>
 			<Add library="m" />
@@ -219,6 +222,8 @@
 			<Add directory="ext/mpdecimal" />
 			<Add directory="../dadbiz++/third-party/discount-2.1.2" />
 		</Linker>
+		<Unit filename="ext/dynamic_library.cpp" />
+		<Unit filename="ext/dynamic_library.h" />
 		<Unit filename="ext/fpdf.cpp" />
 		<Unit filename="ext/fpdf.h" />
 		<Unit filename="ext/lua_socket.cpp" />
@@ -245,6 +250,7 @@
 		<Unit filename="ext/sq_markdown.cpp" />
 		<Unit filename="ext/sq_mix.cpp" />
 		<Unit filename="ext/sq_mongoose.cpp" />
+		<Unit filename="ext/sq_postgresql.cpp" />
 		<Unit filename="ext/sq_rs232.c">
 			<Option compilerVar="CC" />
 		</Unit>