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