|
|
1.1 parser 1: /** @file
2: Parser PgSQL driver.
3:
4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
7:
8: 2001.07.30 using PgSQL 7.1.2
9: */
1.3 ! parser 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.2 2001/07/31 07:47:08 parser 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 "ltdl.h"
18:
19: #define MAX_STRING 0x400
20: #define MAX_NUMBER 20
21:
22: #if _MSC_VER
23: # define snprintf _snprintf
24: # define strcasecmp _stricmp
25: #endif
26:
27: static char *lsplit(char *string, char delim) {
28: if(string) {
29: char *v=strchr(string, delim);
30: if(v) {
31: *v=0;
32: return v+1;
33: }
34: }
35: return 0;
36: }
37:
38: /**
39: PgSQL server driver
40: */
41: class PgSQL_Driver : public SQL_Driver {
42: public:
43:
44: PgSQL_Driver() : SQL_Driver() {
45: }
46:
47: /// get api version
48: int api_version() { return SQL_DRIVER_API_VERSION; }
49: /// initialize driver by loading sql dynamic link library
50: const char *initialize(const char *dlopen_file_spec) {
51: return dlopen_file_spec?
52: dlink(dlopen_file_spec):"client library column is empty";
53: }
54: /** connect
55: @param used_only_in_connect_url
56: format: @b user:pass@host[:port]|[local]/database
57: 3.23.22b
58: Currently the only option for @b character_set_name is cp1251_koi8.
59: WARNING: must be used only to connect, for buffer doesn't live long
60: */
61: void connect(
62: char *used_only_in_connect_url,
63: SQL_Driver_services& services,
64: void **connection ///< output: PGconn *
65: ) {
66: char *user=used_only_in_connect_url;
67: char *host=lsplit(user, '@');
68: char *db=lsplit(host, '/');
69: char *pwd=lsplit(user, ':');
70: char *port=lsplit(host, ':');
71:
72: // _asm int 3;
73: PGconn *pgsql=PQsetdbLogin(
74: strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port,
75: NULL, NULL, db, user, pwd);
76: if(!pgsql)
77: services._throw("PQsetdbLogin failed");
78: if(PQstatus(pgsql)!=CONNECTION_OK)
79: services._throw(PQerrorMessage(pgsql));
80:
81: *(PGconn **)connection=pgsql;
82: }
83: void disconnect(SQL_Driver_services&, void *connection) {
84: PQfinish((PGconn *)connection);
85: }
86: void commit(SQL_Driver_services&, void *) {}
87: void rollback(SQL_Driver_services&, void *) {}
88:
89: bool ping(SQL_Driver_services&, void *connection) {
90: return PQstatus((PGconn *)connection)==CONNECTION_OK;
91: }
92:
93: unsigned int quote(
94: SQL_Driver_services&, void *connection,
95: char *to, const char *from, unsigned int length) {
96: /*
97: 3.23.22b
98: You must allocate the to buffer to be at least length*2+1 bytes long.
99: (In the worse case, each character may need to be encoded as using two bytes,
100: and you need room for the terminating null byte.)
101:
102: it's already UNTAINT_TIMES_BIGGER
103: */
104: unsigned int result=length;
105: while(length--) {
1.3 ! parser 106: switch(*from) {
! 107: case '\'': // "'" -> "''"
1.1 parser 108: *to++='\'';
1.3 ! parser 109: break;
! 110: case '\\': // "\" -> "\\"
! 111: *to++='\'';
! 112: break;
! 113: }
1.1 parser 114: *to++=*from++;
115: }
116: return result;
117: }
118: void query(
119: SQL_Driver_services& services, void *connection,
120: const char *astatement, unsigned long offset, unsigned long limit,
121: SQL_Driver_query_event_handlers& handlers) {
122:
123: PGconn *pgsql=(PGconn *)connection;
124:
125: const char *statement;
126: if(offset || limit) {
127: size_t statement_size=strlen(astatement);
128: char *statement_limited=(char *)services.malloc(
129: statement_size+MAX_NUMBER*2+15/* limit # offset #*/+1);
130: char *cur=statement_limited;
131: memcpy(cur, astatement, statement_size); cur+=statement_size;
132: if(limit)
133: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
134: if(offset)
135: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
136: statement=statement_limited;
137: } else
138: statement=astatement;
139:
140: PGresult *res=PQexec(pgsql, statement);
141: if(!res)
142: services._throw(PQerrorMessage(pgsql));
143:
144: switch(PQresultStatus(res)) {
145: case PGRES_EMPTY_QUERY:
146: PQclear(res);
147: services._throw("no query");
148: break;
149: case PGRES_COMMAND_OK:
150: // empty result: insert|delete|update|...
151: return;
152: case PGRES_TUPLES_OK:
153: break;
154: default:
155: PQclear(res);
156: services._throw("unknown PQexec error");
157: break;
158: }
159:
160: int column_count=PQnfields(res);
161: if(!column_count) {
162: PQclear(res);
163: services._throw("result contains no columns");
164: }
165:
166: for(int i=0; i<column_count; i++){
167: char *name=PQfname(res, i);
168: size_t size=strlen(name);
169: void *ptr=services.malloc(size);
170: memcpy(ptr, name, size);
171: handlers.add_column(ptr, size);
172: }
173:
174: handlers.before_rows();
175:
176: if(unsigned long row_count=(unsigned long)PQntuples(res))
177: for(unsigned long r=0; r<row_count; r++) {
178: handlers.add_row();
179: for(int i=0; i<column_count; i++){
180: size_t size=(size_t)PQgetlength(res, r, i);
181: void *ptr;
182: if(size) {
183: ptr=services.malloc(size);
184: memcpy(ptr, PQgetvalue(res, r, i), size);
185: } else
186: ptr=0;
187: handlers.add_row_cell(ptr, size);
188: }
189: }
190:
191: PQclear(res);
192: }
193:
194: private: // pgsql client library funcs
195:
196: typedef PGconn* (*t_PQsetdbLogin)(
197: const char *pghost,
198: const char *pgport,
199: const char *pgoptions,
200: const char *pgtty,
201: const char *dbName,
202: const char *login,
203: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
204: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
205: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
206: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
207: typedef PGresult *(*t_PQexec)(PGconn *conn,
208: const char *query); t_PQexec PQexec;
209: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
210: typedef int (*t_PQgetlength)(const PGresult *res,
211: int tup_num,
212: int field_num); t_PQgetlength PQgetlength;
213: typedef char* (*t_PQgetvalue)(const PGresult *res,
214: int tup_num,
215: int field_num); t_PQgetvalue PQgetvalue;
216: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
217: typedef char *(*t_PQfname)(const PGresult *res,
218: int field_index); t_PQfname PQfname;
219: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
220: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
221:
222:
223: private: // pgsql client library funcs linking
224:
225: const char *dlink(const char *dlopen_file_spec) {
226: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
227: if(!handle)
228: return "can not open the dynamic link module";
229:
230: #define DSLINK(name, action) \
231: name=(t_##name)lt_dlsym(handle, #name); \
232: if(!name) \
233: action;
234:
235: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
236:
237: DLINK(PQsetdbLogin);
238: DLINK(PQerrorMessage);
239: DLINK(PQstatus);
240: DLINK(PQfinish);
241: DLINK(PQgetvalue);
242: DLINK(PQgetlength);
243: DLINK(PQntuples);
244: DLINK(PQfname);
245: DLINK(PQnfields);
246: DLINK(PQclear);
247: DLINK(PQresultStatus);
248: DLINK(PQexec);
249:
250: return 0;
251: }
252:
253: };
254:
255: extern "C" SQL_Driver *create() {
256: return new PgSQL_Driver();
257: }