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