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