|
|
1.1 parser 1: /** @file
2: Parser PgSQL driver.
3:
1.12 paf 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.1 parser 5:
1.6 paf 6: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 parser 7:
1.28 misha 8: 2007.10.25 using PgSQL 8.1.5
1.1 parser 9: */
1.31 ! misha 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.30 2008-06-26 15:49:40 misha Exp $";
1.1 parser 11:
12: #include "config_includes.h"
13:
14: #include "pa_sql_driver.h"
15:
16: #include <libpq-fe.h>
17: #include <libpq/libpq-fs.h>
18:
1.31 ! misha 19: // from catalog/pg_type.h
! 20: #define BOOLOID 16
! 21: #define INT8OID 20
! 22: #define INT2OID 21
! 23: #define INT4OID 23
1.1 parser 24: #define OIDOID 26
1.31 ! misha 25: #define FLOAT4OID 700
! 26: #define FLOAT8OID 701
! 27: #define DATEOID 1082
! 28: #define TIMEOID 1083
! 29: #define TIMESTAMPOID 1114
! 30: #define TIMESTAMPTZOID 1184
! 31: #define TIMETZOID 1266
! 32: #define NUMERICOID 1700
! 33:
1.1 parser 34: // LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000)
35: // actually writing chunks of that size failed, reduced it twice
36: #define LO_BUFSIZE 0x1000
37:
38:
39: #include "ltdl.h"
40:
1.31 ! misha 41: #define MAX_COLS 500
! 42:
1.1 parser 43: #define MAX_STRING 0x400
44: #define MAX_NUMBER 20
45:
46: #if _MSC_VER
47: # define snprintf _snprintf
48: # define strcasecmp _stricmp
49: #endif
50:
51: #ifndef max
1.30 misha 52: inline int max(int a,int b){ return a>b?a:b; }
1.1 parser 53: inline int min(int a,int b){ return a<b?a:b; }
54: #endif
55:
1.30 misha 56: static char *lsplit(char *string, char delim){
57: if(string){
58: if(char *v=strchr(string, delim)){
1.1 parser 59: *v=0;
60: return v+1;
61: }
1.29 misha 62: }
63: return 0;
1.1 parser 64: }
65:
1.30 misha 66: static char *lsplit(char **string_ref, char delim){
1.29 misha 67: char *result=*string_ref;
1.8 paf 68: char *next=lsplit(*string_ref, delim);
1.29 misha 69: *string_ref=next;
70: return result;
1.8 paf 71: }
72:
1.30 misha 73: static char* rsplit(char* string, char delim){
74: if(string){
75: if(char* v=strrchr(string, delim)){
1.25 paf 76: *v=0;
77: return v+1;
78: }
1.29 misha 79: }
80: return NULL;
1.25 paf 81: }
82:
1.30 misha 83: static void toupper_str(char *out, const char *in, size_t size){
1.18 paf 84: while(size--)
85: *out++=(char)toupper(*in++);
86: }
87:
1.16 paf 88: struct Connection {
89: SQL_Driver_services* services;
90:
91: PGconn *conn;
1.30 misha 92: const char* client_charset;
1.29 misha 93: bool autocommit;
1.31 ! misha 94: bool without_default_transactions;
1.16 paf 95: };
96:
1.1 parser 97: /**
98: PgSQL server driver
99: */
100: class PgSQL_Driver : public SQL_Driver {
101: public:
102:
103: PgSQL_Driver() : SQL_Driver() {
104: }
105:
106: /// get api version
1.30 misha 107: int api_version(){ return SQL_DRIVER_API_VERSION; }
1.29 misha 108:
1.1 parser 109: /// initialize driver by loading sql dynamic link library
1.30 misha 110: const char *initialize(char *dlopen_file_spec){
1.1 parser 111: return dlopen_file_spec?
112: dlink(dlopen_file_spec):"client library column is empty";
113: }
114:
1.16 paf 115: #define throwPQerror connection.services->_throw(PQerrorMessage(connection.conn))
1.8 paf 116: #define PQclear_throw(msg) { \
117: PQclear(res); \
1.16 paf 118: connection.services->_throw(msg); \
1.8 paf 119: }
1.16 paf 120: #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(connection.conn))
1.1 parser 121:
122: /** connect
1.21 paf 123: @param url
1.29 misha 124: format: @b user:pass@host[:port]|[local]/database?
1.30 misha 125: ClientCharset=charset& // transcode by parser
126: charset=value& // transcode by server with 'SET CLIENT_ENCODING=value'
127: datestyle=value& // 'SET DATESTYLE=value' available values are: ISO|SQL|Postgres|European|US|German [default=ISO]
1.31 ! misha 128: autocommit=1& // each transaction is commited automatically (default)
! 129: WithoutDefaultTransaction=0 // 1 -- disable auto commit, 'BEGIN TRAN' at connection start and COMMIT/ROLLBACK at the end [can't be used together with autocommit option]
1.1 parser 130: */
131: void connect(
1.30 misha 132: char* url,
133: SQL_Driver_services& services,
134: void** connection_ref ///< output: Connection*
135: ){
136: char* user=url;
137: char* host=rsplit(user, '@');
138: char* db=lsplit(host, '/');
139: char* pwd=lsplit(user, ':');
140: char* port=lsplit(host, ':');
1.1 parser 141:
1.8 paf 142: char *options=lsplit(db, '?');
143:
1.30 misha 144: char* charset=0;
1.29 misha 145: char* datestyle=0;
1.18 paf 146:
1.30 misha 147: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.16 paf 148: *connection_ref=&connection;
149: connection.services=&services;
1.30 misha 150: connection.client_charset=0;
1.29 misha 151: connection.autocommit=true;
1.31 ! misha 152: connection.without_default_transactions=false;
! 153:
1.16 paf 154: connection.conn=PQsetdbLogin(
1.7 paf 155: (host&&strcasecmp(host, "local")==0)?NULL/* local Unix domain socket */:host, port,
1.1 parser 156: NULL, NULL, db, user, pwd);
1.29 misha 157:
1.16 paf 158: if(!connection.conn)
1.1 parser 159: services._throw("PQsetdbLogin failed");
1.29 misha 160:
161: if(PQstatus(connection.conn)!=CONNECTION_OK)
1.1 parser 162: throwPQerror;
163:
1.29 misha 164: while(options){
165: if(char *key=lsplit(&options, '&')){
166: if(*key){
167: if(char *value=lsplit(key, '=')){
1.31 ! misha 168: if(strcmp(key, "ClientCharset")==0){
1.20 paf 169: toupper_str(value, value, strlen(value));
1.30 misha 170: connection.client_charset=value;
1.31 ! misha 171: } else if(strcasecmp(key, "charset")==0){
1.30 misha 172: charset=value;
173: } else if(strcasecmp(key, "datestyle")==0){
1.8 paf 174: datestyle=value;
1.29 misha 175: } else if(strcasecmp(key, "autocommit")==0){
1.31 ! misha 176: if(connection.without_default_transactions)
! 177: services._throw("options WithoutDefaultTransaction and autocommit can't be used together");
1.29 misha 178: if(atoi(value)==0)
179: connection.autocommit=false;
1.31 ! misha 180: } else if(strcmp(key, "WithoutDefaultTransaction")==0){
! 181: if(!connection.autocommit)
! 182: services._throw("options WithoutDefaultTransaction and autocommit can't be used together");
! 183: if(atoi(value)==1){
! 184: connection.without_default_transactions=true;
1.29 misha 185: connection.autocommit=false;
1.31 ! misha 186: }
1.8 paf 187: } else
188: services._throw("unknown connect option" /*key*/);
189: } else
190: services._throw("connect option without =value" /*key*/);
191: }
192: }
193: }
194:
1.30 misha 195: if(charset){
196: char statement[MAX_STRING]="SET CLIENT_ENCODING=";
197: strncat(statement, charset, MAX_STRING);
1.26 misha 198:
1.30 misha 199: _execute_cmd(connection, statement);
1.8 paf 200: }
201:
1.29 misha 202: if(datestyle){
1.30 misha 203: char statement[MAX_STRING]="SET DATESTYLE=";
1.29 misha 204: strncat(statement, datestyle, MAX_STRING);
1.26 misha 205:
1.30 misha 206: _execute_cmd(connection, statement);
1.8 paf 207: }
208:
1.30 misha 209: _begin_transaction(connection);
1.1 parser 210: }
1.29 misha 211:
1.30 misha 212: void disconnect(void *aconnection){
1.16 paf 213: Connection& connection=*static_cast<Connection*>(aconnection);
1.29 misha 214: PQfinish(connection.conn);
1.16 paf 215: connection.conn=0;
1.1 parser 216: }
1.29 misha 217:
1.30 misha 218: void commit(void *aconnection){
219: Connection& connection=*static_cast<Connection*>(aconnection);
1.31 ! misha 220: if(!connection.without_default_transactions){
1.30 misha 221: _execute_cmd(connection, "COMMIT");
222: }
223: _begin_transaction(connection);
1.1 parser 224: }
1.29 misha 225:
1.30 misha 226: void rollback(void *aconnection){
227: Connection& connection=*static_cast<Connection*>(aconnection);
1.31 ! misha 228: if(!connection.without_default_transactions){
1.30 misha 229: _execute_cmd(connection, "ROLLBACK");
230: }
231: _begin_transaction(connection);
1.1 parser 232: }
233:
1.16 paf 234: bool ping(void *aconnection) {
235: Connection& connection=*static_cast<Connection*>(aconnection);
236: return PQstatus(connection.conn)==CONNECTION_OK;
1.1 parser 237: }
238:
1.30 misha 239: const char* quote(void *aconnection, const char *from, unsigned int length){
1.16 paf 240: Connection& connection=*static_cast<Connection*>(aconnection);
241:
242: char *result=(char*)connection.services->malloc_atomic(length*2+1);
1.29 misha 243: int err=0;
1.30 misha 244: PQescapeStringConn(connection.conn, result, from, length, &err);
1.13 paf 245: return result;
1.26 misha 246: }
247:
1.16 paf 248: void query(void *aconnection,
1.30 misha 249: const char *astatement,
250: size_t placeholders_count, Placeholder* placeholders,
251: unsigned long offset, unsigned long limit,
252: SQL_Driver_query_event_handlers& handlers
253: ){
1.16 paf 254: Connection& connection=*static_cast<Connection*>(aconnection);
255: SQL_Driver_services& services=*connection.services;
256: PGconn *conn=connection.conn;
1.22 paf 257:
1.31 ! misha 258: const char* client_charset=connection.client_charset;
! 259: const char* request_charset=services.request_charset();
! 260: bool transcode_needed=client_charset && strcmp(client_charset, request_charset)!=0;
1.30 misha 261:
1.26 misha 262: const char** paramValues;
263: if(placeholders_count>0){
1.30 misha 264: int binds_size=sizeof(char)*placeholders_count;
1.26 misha 265: paramValues = static_cast<const char**>(services.malloc_atomic(binds_size));
1.30 misha 266: _bind_parameters(placeholders_count, placeholders, paramValues, connection, transcode_needed);
1.26 misha 267: }
1.1 parser 268:
1.30 misha 269: if(transcode_needed){
1.31 ! misha 270: // transcode query from $request:charset to ?ClientCharset
1.30 misha 271: size_t length=strlen(astatement);
272: services.transcode(astatement, length,
273: astatement, length,
1.31 ! misha 274: request_charset,
! 275: client_charset);
1.18 paf 276: }
277:
1.30 misha 278: const char *statement=_preprocess_statement(connection, astatement, offset, limit);
279: // error after prepare?
1.1 parser 280:
1.26 misha 281: PGresult *res;
282: if(placeholders_count>0){
283: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
284: } else {
285: res=PQexec(conn, statement);
286: }
1.1 parser 287: if(!res)
288: throwPQerror;
289:
290: switch(PQresultStatus(res)) {
1.30 misha 291: case PGRES_EMPTY_QUERY:
292: PQclear_throw("no query");
293: break;
294: case PGRES_COMMAND_OK: // empty result: insert|delete|update|...
295: PQclear(res);
296: return;
297: case PGRES_TUPLES_OK:
298: break;
299: default:
300: PQclear_throwPQerror;
301: break;
1.1 parser 302: }
303:
304: int column_count=PQnfields(res);
305: if(!column_count)
306: PQclear_throw("result contains no columns");
307:
1.9 paf 308: bool failed=false;
309: SQL_Error sql_error;
310: #define CHECK(afailed) \
311: if(afailed) { \
312: failed=true; \
313: goto cleanup; \
314: }
315:
1.31 ! misha 316: if(column_count>MAX_COLS)
! 317: column_count=MAX_COLS;
! 318:
! 319: unsigned int column_types[MAX_COLS];
! 320: bool transcode_column[MAX_COLS];
! 321:
1.1 parser 322: for(int i=0; i<column_count; i++){
323: char *name=PQfname(res, i);
1.18 paf 324: size_t length=strlen(name);
1.31 ! misha 325: column_types[i]=PQftype(res, i);
! 326: switch(column_types[i]){
! 327: case BOOLOID:
! 328: case INT8OID:
! 329: case INT2OID:
! 330: case INT4OID:
! 331: case FLOAT4OID:
! 332: case FLOAT8OID:
! 333: case DATEOID:
! 334: case TIMEOID:
! 335: case TIMESTAMPOID:
! 336: case TIMESTAMPTZOID:
! 337: case TIMETZOID:
! 338: case NUMERICOID:
! 339: transcode_column[i]=false;
! 340: break;
! 341: default:
! 342: transcode_column[i]=transcode_needed;
! 343: break;
! 344: }
1.19 paf 345: char* strm=(char*)services.malloc(length+1);
346: memcpy(strm, name, length+1);
347: const char* str=strm;
1.18 paf 348:
1.30 misha 349: if(transcode_needed)
1.31 ! misha 350: // transcode column name from ?ClientCharset to $request:charset
1.18 paf 351: services.transcode(str, length,
352: str, length,
1.31 ! misha 353: client_charset,
! 354: request_charset);
1.18 paf 355:
356: CHECK(handlers.add_column(sql_error, str, length));
1.1 parser 357: }
358:
1.9 paf 359: CHECK(handlers.before_rows(sql_error));
1.1 parser 360:
361: if(unsigned long row_count=(unsigned long)PQntuples(res))
362: for(unsigned long r=0; r<row_count; r++) {
1.9 paf 363: CHECK(handlers.add_row(sql_error));
1.1 parser 364: for(int i=0; i<column_count; i++){
365: const char *cell=PQgetvalue(res, r, i);
1.18 paf 366: size_t length;
1.19 paf 367: const char* str;
1.1 parser 368:
1.31 ! misha 369: switch(column_types[i]){
! 370: case OIDOID:
! 371: {
! 372: char *error_pos=0;
! 373: Oid oid=cell?atoi(cell):0;
! 374: int fd=lo_open(conn, oid, INV_READ);
! 375: if(fd>=0){
! 376: // seek to end
! 377: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
! 378: PQclear_throwPQerror;
! 379: // get length
! 380: int size_tell=lo_tell(conn, fd);
! 381: if(size_tell<0)
! 382: PQclear_throwPQerror;
! 383: // seek to begin
! 384: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
! 385: PQclear_throwPQerror;
! 386: length=(size_t)size_tell;
! 387: if(length){
! 388: // read
! 389: char* strm=(char*)services.malloc(length+1);
! 390: if(!lo_read_ex(conn, fd, strm, size_tell))
! 391: PQclear_throw("lo_read can not read all bytes of object");
! 392: strm[length]=0;
! 393: str=strm;
! 394: } else
! 395: str=0;
! 396: if(lo_close(conn, fd)<0)
! 397: PQclear_throwPQerror;
! 398: } else
! 399: PQclear_throwPQerror;
! 400: }
! 401: default:
! 402: // normal column, read it normally
! 403: length=(size_t)PQgetlength(res, r, i);
! 404: if(length){
1.19 paf 405: char* strm=(char*)services.malloc(length+1);
1.31 ! misha 406: memcpy(strm, cell, length+1);
1.19 paf 407: str=strm;
1.1 parser 408: } else
1.13 paf 409: str=0;
1.1 parser 410: }
1.18 paf 411:
1.31 ! misha 412: if(str && length && transcode_column[i]){
! 413: //services._throw("tr");
1.30 misha 414: // transcode cell value from ?ClientCharset to $request:charset
415: services.transcode(str, length,
416: str, length,
1.31 ! misha 417: client_charset,
! 418: request_charset);
1.18 paf 419: }
420:
421: CHECK(handlers.add_row_cell(sql_error, str, length));
1.1 parser 422: }
423: }
1.9 paf 424: cleanup:
1.1 parser 425: PQclear(res);
1.9 paf 426: if(failed)
427: services._throw(sql_error);
1.31 ! misha 428:
! 429: commit(aconnection);
1.1 parser 430: }
431:
1.30 misha 432: private:
433: void _bind_parameters(
434: size_t placeholders_count,
435: Placeholder* placeholders,
436: const char** paramValues,
437: Connection& connection,
438: bool transcode_needed
439: ){
440: for(size_t i=0; i<placeholders_count; i++){
1.27 misha 441: Placeholder& ph=placeholders[i];
1.30 misha 442: if(transcode_needed){
1.27 misha 443: size_t name_length;
1.30 misha 444: size_t value_length;
1.27 misha 445: connection.services->transcode(ph.name, strlen(ph.name),
446: ph.name, name_length,
447: connection.services->request_charset(),
1.30 misha 448: connection.client_charset);
1.27 misha 449:
450: if(ph.value) {
451: connection.services->transcode(ph.value, strlen(ph.value),
452: ph.value, value_length,
453: connection.services->request_charset(),
1.30 misha 454: connection.client_charset);
1.27 misha 455: }
456: }
1.30 misha 457: int name_numner=atoi(ph.name);
458: if(name_numner <= 0 || name_numner > placeholders_count)
1.27 misha 459: connection.services->_throw("bad bind parameter key");
1.30 misha 460:
461: paramValues[name_numner-1]=ph.value;
1.27 misha 462: }
463: }
464:
465:
1.26 misha 466: /**
1.30 misha 467: Executes a query and throw away the result.
1.26 misha 468: */
1.30 misha 469: void _execute_cmd(const Connection& connection, const char *query){
1.26 misha 470: if(PGresult *res=PQexec(connection.conn, query))
471: PQclear(res); // throw out the result [don't need but must call]
472: else
473: throwPQerror;
474: }
475:
1.30 misha 476: void _begin_transaction(Connection& connection){
1.31 ! misha 477: if(!connection.without_default_transactions)
1.30 misha 478: _execute_cmd(connection, "BEGIN");
1.1 parser 479: }
480:
1.30 misha 481: const char *_preprocess_statement(
482: Connection& connection,
483: const char *astatement,
484: unsigned long offset,
485: unsigned long limit
486: ){
1.16 paf 487: PGconn *conn=connection.conn;
488:
1.1 parser 489: size_t statement_size=strlen(astatement);
490:
1.16 paf 491: char *result=(char *)connection.services->malloc(statement_size
1.1 parser 492: +MAX_NUMBER*2+15 // limit # offset #
493: +MAX_STRING // in case of short 'strings'
494: +1);
495: // offset & limit -> suffixes
496: const char *o;
1.30 misha 497: if(offset || limit!=SQL_NO_LIMIT){
1.1 parser 498: char *cur=result;
499: memcpy(cur, astatement, statement_size); cur+=statement_size;
1.30 misha 500: if(limit!=SQL_NO_LIMIT)
1.1 parser 501: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
502: if(offset)
503: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
504: o=result;
505: } else
506: o=astatement;
507:
508: // /**xxx**/'literal' -> oid
509: char *n=result;
510: while(*o) {
511: if(
512: o[0]=='/' &&
513: o[1]=='*' &&
514: o[2]=='*') { // name start
1.15 paf 515: const char* saved_o=o;
1.1 parser 516: o+=3;
517: while(*o)
518: if(
519: o[0]=='*' &&
520: o[1]=='*' &&
521: o[2]=='/' &&
522: o[3]=='\'') { // name end
1.15 paf 523: saved_o=0; // found, marking that
1.1 parser 524: o+=4;
525: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
526: if(oid==InvalidOid)
527: throwPQerror;
528: int fd=lo_open(conn, oid, INV_WRITE);
529: if(fd>=0) {
530: const char *start=o;
531: bool escaped=false;
532: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
533: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
534: if(escaped) {
535: // write pending, skip "\" or "'"
536: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 537: connection.services->_throw("lo_write could not write all bytes of object (1)");
1.1 parser 538: start=++o;
539: } else
540: o++;
541: }
542: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 543: connection.services->_throw("lo_write can not write all bytes of object (2)");
1.1 parser 544: if(lo_close(conn, fd)<0)
545: throwPQerror;
546: } else
547: throwPQerror;
548: if(*o)
549: o++; // skip "'"
550:
551: n+=snprintf(n, MAX_NUMBER, "%u", oid);
552: break;
553: } else
554: o++; // /**skip**/'xxx'
1.15 paf 555: if(saved_o) {
556: o=saved_o;
557: *n++=*o++;
558: }
1.1 parser 559: } else
560: *n++=*o++;
561: }
562: *n=0;
563:
564: return result;
565: }
566:
567: private: // lo_read/write exchancements
568:
569: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 misha 570: return lo_rw_method (conn, fd, buf, len, lo_read);
1.1 parser 571: }
572:
573: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 misha 574: return lo_rw_method (conn, fd, buf, len, lo_write);
575: }
576:
577: bool lo_rw_method(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len, int (*lo_func)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len)) {
578: int size_op;
579: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
580: buf+=size_op;
1.29 misha 581: len-=size_op;
1.1 parser 582: }
583: return len==0;
584: }
585:
586: private: // conn client library funcs
587:
588: typedef PGconn* (*t_PQsetdbLogin)(
589: const char *pghost,
590: const char *pgport,
591: const char *pgoptions,
592: const char *pgtty,
593: const char *dbName,
594: const char *login,
595: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
596: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
597: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
598: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
599: typedef PGresult *(*t_PQexec)(PGconn *conn,
1.29 misha 600: const char *query); t_PQexec PQexec;
1.26 misha 601: typedef PGresult *(*t_PQexecParams)(
1.29 misha 602: PGconn *conn,
603: const char *query,
604: int nParams,
605: const Oid *paramTypes,
606: const char * const *paramValues,
607: const int *paramLengths,
608: const int *paramFormats,
609: int resultFormat); t_PQexecParams PQexecParams;
1.26 misha 610:
1.1 parser 611: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
612: typedef int (*t_PQgetlength)(const PGresult *res,
1.29 misha 613: int tup_num,
614: int field_num); t_PQgetlength PQgetlength;
1.1 parser 615: typedef char* (*t_PQgetvalue)(const PGresult *res,
1.29 misha 616: int tup_num,
617: int field_num); t_PQgetvalue PQgetvalue;
1.30 misha 618: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
1.1 parser 619: typedef char *(*t_PQfname)(const PGresult *res,
620: int field_index); t_PQfname PQfname;
621: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
622: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
623:
624: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
625:
1.26 misha 626: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
1.29 misha 627: char *to, const char *from, size_t length,
628: int *error); t_PQescapeStringConn PQescapeStringConn;
1.26 misha 629:
1.1 parser 630: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
631: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
632: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
633: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
634: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
635: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
636: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
637: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
638: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
639: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
640:
641: private: // conn client library funcs linking
642:
643: const char *dlink(const char *dlopen_file_spec) {
1.11 paf 644: if(lt_dlinit())
645: return lt_dlerror();
1.29 misha 646: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
647: if(!handle)
1.1 parser 648: return "can not open the dynamic link module";
649:
650: #define DSLINK(name, action) \
651: name=(t_##name)lt_dlsym(handle, #name); \
652: if(!name) \
653: action;
654:
655: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
656:
657: DLINK(PQsetdbLogin);
658: DLINK(PQerrorMessage);
659: DLINK(PQstatus);
660: DLINK(PQfinish);
661: DLINK(PQgetvalue);
662: DLINK(PQgetlength);
663: DLINK(PQntuples);
664: DLINK(PQfname);
665: DLINK(PQnfields);
666: DLINK(PQclear);
667: DLINK(PQresultStatus);
668: DLINK(PQexec);
1.26 misha 669: DLINK(PQexecParams);
1.1 parser 670: DLINK(PQftype);
1.26 misha 671: DLINK(PQescapeStringConn);
1.1 parser 672: DLINK(lo_open); DLINK(lo_close);
673: DLINK(lo_read); DLINK(lo_write);
674: DLINK(lo_lseek); DLINK(lo_creat);
675: DLINK(lo_tell); DLINK(lo_unlink);
676: DLINK(lo_import); DLINK(lo_export);
677:
678: return 0;
679: }
680: };
681:
682: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
683: return new PgSQL_Driver();
1.2 paf 684: }