1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Authors: Edin Kadribasic <edink@emini.dk> |
16 : | Ilia Alshanestsky <ilia@prohost.org> |
17 : | Wez Furlong <wez@php.net> |
18 : +----------------------------------------------------------------------+
19 : */
20 :
21 : /* $Id: pgsql_driver.c 290214 2009-11-04 19:32:27Z mbeccati $ */
22 :
23 : #ifdef HAVE_CONFIG_H
24 : #include "config.h"
25 : #endif
26 :
27 : #include "php.h"
28 : #include "php_ini.h"
29 : #include "ext/standard/info.h"
30 : #include "pdo/php_pdo.h"
31 : #include "pdo/php_pdo_driver.h"
32 :
33 : #undef PACKAGE_BUGREPORT
34 : #undef PACKAGE_NAME
35 : #undef PACKAGE_STRING
36 : #undef PACKAGE_TARNAME
37 : #undef PACKAGE_VERSION
38 : #include "pg_config.h" /* needed for PG_VERSION */
39 : #include "php_pdo_pgsql.h"
40 : #include "php_pdo_pgsql_int.h"
41 : #include "zend_exceptions.h"
42 :
43 : static char * _pdo_pgsql_trim_message(const char *message, int persistent)
44 324 : {
45 324 : register int i = strlen(message)-1;
46 : char *tmp;
47 :
48 324 : if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
49 0 : --i;
50 : }
51 972 : while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
52 324 : --i;
53 : }
54 324 : ++i;
55 324 : tmp = pemalloc(i + 1, persistent);
56 324 : memcpy(tmp, message, i);
57 324 : tmp[i] = '\0';
58 :
59 324 : return tmp;
60 : }
61 :
62 : int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */
63 324 : {
64 324 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
65 324 : pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
66 324 : pdo_pgsql_error_info *einfo = &H->einfo;
67 324 : char *errmsg = PQerrorMessage(H->server);
68 :
69 324 : einfo->errcode = errcode;
70 324 : einfo->file = file;
71 324 : einfo->line = line;
72 :
73 324 : if (einfo->errmsg) {
74 204 : pefree(einfo->errmsg, dbh->is_persistent);
75 204 : einfo->errmsg = NULL;
76 : }
77 :
78 324 : if (sqlstate == NULL) {
79 0 : strcpy(*pdo_err, "HY000");
80 : }
81 : else {
82 324 : strcpy(*pdo_err, sqlstate);
83 : }
84 :
85 324 : if (errmsg) {
86 324 : einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
87 : }
88 :
89 324 : if (!dbh->methods) {
90 0 : zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
91 : *pdo_err, einfo->errcode, einfo->errmsg);
92 : }
93 :
94 324 : return errcode;
95 : }
96 : /* }}} */
97 :
98 : static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
99 47 : {
100 : /* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
101 47 : }
102 : /* }}} */
103 :
104 : static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */
105 18 : {
106 18 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
107 18 : pdo_pgsql_error_info *einfo = &H->einfo;
108 :
109 18 : if (einfo->errcode) {
110 18 : add_next_index_long(info, einfo->errcode);
111 18 : add_next_index_string(info, einfo->errmsg, 1);
112 : }
113 :
114 18 : return 1;
115 : }
116 : /* }}} */
117 :
118 : /* {{{ pdo_pgsql_create_lob_stream */
119 : static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
120 1 : {
121 1 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
122 1 : return lo_write(self->conn, self->lfd, (char*)buf, count);
123 : }
124 :
125 : static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
126 2 : {
127 2 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
128 2 : return lo_read(self->conn, self->lfd, buf, count);
129 : }
130 :
131 : static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC)
132 3 : {
133 3 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
134 3 : pdo_dbh_t *dbh = self->dbh;
135 :
136 3 : if (close_handle) {
137 3 : lo_close(self->conn, self->lfd);
138 : }
139 3 : efree(self);
140 3 : php_pdo_dbh_delref(dbh TSRMLS_CC);
141 3 : return 0;
142 : }
143 :
144 : static int pgsql_lob_flush(php_stream *stream TSRMLS_DC)
145 3 : {
146 3 : return 0;
147 : }
148 :
149 : static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence,
150 : off_t *newoffset TSRMLS_DC)
151 0 : {
152 0 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
153 0 : int pos = lo_lseek(self->conn, self->lfd, offset, whence);
154 0 : *newoffset = pos;
155 0 : return pos >= 0 ? 0 : -1;
156 : }
157 :
158 : php_stream_ops pdo_pgsql_lob_stream_ops = {
159 : pgsql_lob_write,
160 : pgsql_lob_read,
161 : pgsql_lob_close,
162 : pgsql_lob_flush,
163 : "pdo_pgsql lob stream",
164 : pgsql_lob_seek,
165 : NULL,
166 : NULL,
167 : NULL
168 : };
169 :
170 : php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC)
171 3 : {
172 : php_stream *stm;
173 3 : struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
174 3 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
175 :
176 3 : self->dbh = dbh;
177 3 : self->lfd = lfd;
178 3 : self->oid = oid;
179 3 : self->conn = H->server;
180 :
181 3 : stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
182 :
183 3 : if (stm) {
184 3 : php_pdo_dbh_addref(dbh TSRMLS_CC);
185 3 : return stm;
186 : }
187 :
188 0 : efree(self);
189 0 : return NULL;
190 : }
191 : /* }}} */
192 :
193 : static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
194 119 : {
195 119 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
196 119 : if (H) {
197 119 : if (H->server) {
198 119 : PQfinish(H->server);
199 119 : H->server = NULL;
200 : }
201 119 : if (H->einfo.errmsg) {
202 119 : pefree(H->einfo.errmsg, dbh->is_persistent);
203 119 : H->einfo.errmsg = NULL;
204 : }
205 119 : pefree(H, dbh->is_persistent);
206 119 : dbh->driver_data = NULL;
207 : }
208 119 : return 0;
209 : }
210 : /* }}} */
211 :
212 : static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
213 144 : {
214 144 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
215 144 : pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
216 : int scrollable;
217 : #if HAVE_PQPREPARE
218 : int ret;
219 144 : char *nsql = NULL;
220 144 : int nsql_len = 0;
221 144 : int emulate = 0;
222 : #endif
223 :
224 144 : S->H = H;
225 144 : stmt->driver_data = S;
226 144 : stmt->methods = &pgsql_stmt_methods;
227 :
228 144 : scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
229 : PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
230 :
231 144 : if (scrollable) {
232 0 : if (S->cursor_name) {
233 0 : efree(S->cursor_name);
234 : }
235 : /* TODO: check how scrollable cursors related to prepared statements */
236 0 : spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
237 : }
238 :
239 : #if HAVE_PQPREPARE
240 :
241 144 : if (driver_options) {
242 28 : if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 ||
243 : pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
244 12 : emulate = 1;
245 : }
246 : } else {
247 116 : emulate = H->disable_native_prepares || H->emulate_prepares;
248 : }
249 :
250 144 : if (!emulate && PQprotocolVersion(H->server) > 2) {
251 131 : stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
252 131 : stmt->named_rewrite_template = "$%d";
253 131 : ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
254 :
255 131 : if (ret == 1) {
256 : /* query was re-written */
257 38 : sql = nsql;
258 93 : } else if (ret == -1) {
259 : /* couldn't grok it */
260 0 : strcpy(dbh->error_code, stmt->error_code);
261 0 : return 0;
262 : }
263 :
264 131 : spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
265 : /* that's all for now; we'll defer the actual prepare until the first execute call */
266 :
267 131 : if (nsql) {
268 38 : S->query = nsql;
269 : } else {
270 93 : S->query = estrdup(sql);
271 : }
272 :
273 131 : return 1;
274 : }
275 : #endif
276 :
277 13 : stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
278 13 : return 1;
279 : }
280 :
281 : static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
282 509 : {
283 509 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
284 : PGresult *res;
285 509 : long ret = 1;
286 : ExecStatusType qs;
287 :
288 509 : if (!(res = PQexec(H->server, sql))) {
289 : /* fatal error */
290 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
291 0 : return -1;
292 : }
293 509 : qs = PQresultStatus(res);
294 509 : if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
295 310 : pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
296 310 : PQclear(res);
297 310 : return -1;
298 : }
299 199 : H->pgoid = PQoidValue(res);
300 : #if HAVE_PQCMDTUPLES
301 199 : ret = atol(PQcmdTuples(res));
302 : #endif
303 199 : PQclear(res);
304 :
305 199 : return ret;
306 : }
307 :
308 : static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
309 16 : {
310 : unsigned char *escaped;
311 16 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
312 :
313 16 : switch (paramtype) {
314 : case PDO_PARAM_LOB:
315 : /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
316 : #ifdef HAVE_PQESCAPE_BYTEA_CONN
317 0 : escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, quotedlen);
318 : #else
319 : escaped = PQescapeBytea(unquoted, unquotedlen, quotedlen);
320 : #endif
321 0 : *quotedlen += 1;
322 0 : *quoted = emalloc(*quotedlen + 1);
323 0 : memcpy((*quoted)+1, escaped, *quotedlen-2);
324 0 : (*quoted)[0] = '\'';
325 0 : (*quoted)[*quotedlen-1] = '\'';
326 0 : (*quoted)[*quotedlen] = '\0';
327 0 : PQfreemem(escaped);
328 0 : break;
329 : default:
330 16 : *quoted = safe_emalloc(2, unquotedlen, 3);
331 16 : (*quoted)[0] = '\'';
332 : #ifndef HAVE_PQESCAPE_CONN
333 : *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen);
334 : #else
335 16 : *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
336 : #endif
337 16 : (*quoted)[*quotedlen + 1] = '\'';
338 16 : (*quoted)[*quotedlen + 2] = '\0';
339 16 : *quotedlen += 2;
340 : }
341 16 : return 1;
342 : }
343 :
344 : static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC)
345 0 : {
346 0 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
347 0 : char *id = NULL;
348 :
349 0 : if (name == NULL) {
350 0 : if (H->pgoid == InvalidOid) {
351 0 : return NULL;
352 : }
353 0 : *len = spprintf(&id, 0, "%ld", (long) H->pgoid);
354 : } else {
355 : PGresult *res;
356 : ExecStatusType status;
357 : #ifdef HAVE_PQEXECPARAMS
358 : const char *q[1];
359 0 : q[0] = name;
360 0 : res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
361 : #else
362 : char *name_escaped, *q;
363 : size_t l = strlen(name);
364 :
365 : name_escaped = safe_emalloc(l, 2, 1);
366 : #ifndef HAVE_PQESCAPE_CONN
367 : PQescapeString(name_escaped, name, l);
368 : #else
369 : PQescapeStringConn(H->server, name_escaped, name, l, NULL);
370 : #endif
371 : spprintf(&q, 0, "SELECT CURRVAL('%s')", name_escaped);
372 : res = PQexec(H->server, q);
373 : efree(name_escaped);
374 : efree(q);
375 : #endif
376 0 : status = PQresultStatus(res);
377 :
378 0 : if (res && (status == PGRES_TUPLES_OK)) {
379 0 : id = estrdup((char *)PQgetvalue(res, 0, 0));
380 0 : *len = PQgetlength(res, 0, 0);
381 : } else {
382 0 : pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
383 : }
384 :
385 0 : if (res) {
386 0 : PQclear(res);
387 : }
388 : }
389 0 : return id;
390 : }
391 :
392 : static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
393 2 : {
394 2 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
395 :
396 2 : switch (attr) {
397 : case PDO_ATTR_CLIENT_VERSION:
398 1 : ZVAL_STRING(return_value, PG_VERSION, 1);
399 1 : break;
400 :
401 : case PDO_ATTR_SERVER_VERSION:
402 : #ifdef HAVE_PQPROTOCOLVERSION
403 1 : if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
404 1 : ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1);
405 : } else /* emulate above via a query */
406 : #endif
407 : {
408 0 : PGresult *res = PQexec(H->server, "SELECT VERSION()");
409 0 : if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
410 0 : ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1);
411 : }
412 :
413 0 : if (res) {
414 0 : PQclear(res);
415 : }
416 : }
417 1 : break;
418 :
419 : case PDO_ATTR_CONNECTION_STATUS:
420 0 : switch (PQstatus(H->server)) {
421 : case CONNECTION_STARTED:
422 0 : ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1);
423 0 : break;
424 :
425 : case CONNECTION_MADE:
426 : case CONNECTION_OK:
427 0 : ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1);
428 0 : break;
429 :
430 : case CONNECTION_AWAITING_RESPONSE:
431 0 : ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1);
432 0 : break;
433 :
434 : case CONNECTION_AUTH_OK:
435 0 : ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1, 1);
436 0 : break;
437 : #ifdef CONNECTION_SSL_STARTUP
438 : case CONNECTION_SSL_STARTUP:
439 : ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1);
440 : break;
441 : #endif
442 : case CONNECTION_SETENV:
443 0 : ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1);
444 0 : break;
445 :
446 : case CONNECTION_BAD:
447 : default:
448 0 : ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1);
449 : break;
450 : }
451 0 : break;
452 :
453 : case PDO_ATTR_SERVER_INFO: {
454 0 : int spid = PQbackendPID(H->server);
455 : char *tmp;
456 : #ifdef HAVE_PQPROTOCOLVERSION
457 0 : spprintf(&tmp, 0,
458 : "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
459 : spid,
460 : (char*)PQparameterStatus(H->server, "client_encoding"),
461 : (char*)PQparameterStatus(H->server, "is_superuser"),
462 : (char*)PQparameterStatus(H->server, "session_authorization"),
463 : (char*)PQparameterStatus(H->server, "DateStyle"));
464 : #else
465 : spprintf(&tmp, 0, "PID: %d", spid);
466 : #endif
467 0 : ZVAL_STRING(return_value, tmp, 0);
468 : }
469 0 : break;
470 :
471 : default:
472 0 : return 0;
473 : }
474 :
475 2 : return 1;
476 : }
477 :
478 : /* {{{ */
479 : static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
480 0 : {
481 0 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
482 0 : if (PQstatus(H->server) == CONNECTION_BAD) {
483 0 : PQreset(H->server);
484 : }
485 0 : return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
486 : }
487 : /* }}} */
488 :
489 : static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
490 14 : {
491 14 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
492 : PGresult *res;
493 14 : int ret = 1;
494 :
495 14 : res = PQexec(H->server, cmd);
496 :
497 14 : if (PQresultStatus(res) != PGRES_COMMAND_OK) {
498 0 : pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
499 0 : ret = 0;
500 : }
501 :
502 14 : PQclear(res);
503 14 : return ret;
504 : }
505 :
506 : static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
507 7 : {
508 7 : return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
509 : }
510 :
511 : static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
512 2 : {
513 2 : return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
514 : }
515 :
516 : static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
517 5 : {
518 5 : return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
519 : }
520 :
521 : /* {{{ proto string PDO::pgsqlLOBCreate()
522 : Creates a new large object, returning its identifier. Must be called inside a transaction. */
523 : static PHP_METHOD(PDO, pgsqlLOBCreate)
524 1 : {
525 : pdo_dbh_t *dbh;
526 : pdo_pgsql_db_handle *H;
527 : Oid lfd;
528 :
529 1 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
530 1 : PDO_CONSTRUCT_CHECK;
531 :
532 1 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
533 1 : lfd = lo_creat(H->server, INV_READ|INV_WRITE);
534 :
535 1 : if (lfd != InvalidOid) {
536 : char *buf;
537 1 : spprintf(&buf, 0, "%lu", (long) lfd);
538 1 : RETURN_STRING(buf, 0);
539 : }
540 :
541 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
542 0 : RETURN_FALSE;
543 : }
544 : /* }}} */
545 :
546 : /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
547 : Opens an existing large object stream. Must be called inside a transaction. */
548 : static PHP_METHOD(PDO, pgsqlLOBOpen)
549 1 : {
550 : pdo_dbh_t *dbh;
551 : pdo_pgsql_db_handle *H;
552 : Oid oid;
553 : int lfd;
554 : char *oidstr;
555 : int oidstrlen;
556 1 : char *modestr = "rb";
557 : int modestrlen;
558 1 : int mode = INV_READ;
559 : char *end_ptr;
560 :
561 1 : if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
562 : &oidstr, &oidstrlen, &modestr, &modestrlen)) {
563 0 : RETURN_FALSE;
564 : }
565 :
566 1 : oid = (Oid)strtoul(oidstr, &end_ptr, 10);
567 1 : if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
568 0 : RETURN_FALSE;
569 : }
570 :
571 1 : if (strpbrk(modestr, "+w")) {
572 1 : mode = INV_READ|INV_WRITE;
573 : }
574 :
575 1 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
576 1 : PDO_CONSTRUCT_CHECK;
577 :
578 1 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
579 :
580 1 : lfd = lo_open(H->server, oid, mode);
581 :
582 1 : if (lfd >= 0) {
583 1 : php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
584 1 : if (stream) {
585 1 : php_stream_to_zval(stream, return_value);
586 1 : return;
587 : }
588 : } else {
589 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
590 : }
591 0 : RETURN_FALSE;
592 : }
593 : /* }}} */
594 :
595 : /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
596 : Deletes the large object identified by oid. Must be called inside a transaction. */
597 : static PHP_METHOD(PDO, pgsqlLOBUnlink)
598 1 : {
599 : pdo_dbh_t *dbh;
600 : pdo_pgsql_db_handle *H;
601 : Oid oid;
602 : char *oidstr, *end_ptr;
603 : int oidlen;
604 :
605 1 : if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
606 : &oidstr, &oidlen)) {
607 0 : RETURN_FALSE;
608 : }
609 :
610 1 : oid = (Oid)strtoul(oidstr, &end_ptr, 10);
611 1 : if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
612 0 : RETURN_FALSE;
613 : }
614 :
615 1 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
616 1 : PDO_CONSTRUCT_CHECK;
617 :
618 1 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
619 :
620 1 : if (1 == lo_unlink(H->server, oid)) {
621 1 : RETURN_TRUE;
622 : }
623 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
624 0 : RETURN_FALSE;
625 : }
626 : /* }}} */
627 :
628 :
629 : static zend_function_entry dbh_methods[] = {
630 : PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
631 : PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
632 : PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
633 : {NULL, NULL, NULL}
634 : };
635 :
636 : static zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
637 1 : {
638 1 : switch (kind) {
639 : case PDO_DBH_DRIVER_METHOD_KIND_DBH:
640 1 : return dbh_methods;
641 : default:
642 0 : return NULL;
643 : }
644 : }
645 :
646 : static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
647 9 : {
648 9 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
649 :
650 9 : switch (attr) {
651 : #if HAVE_PQPREPARE
652 : case PDO_ATTR_EMULATE_PREPARES:
653 5 : H->emulate_prepares = Z_LVAL_P(val);
654 5 : return 1;
655 : case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
656 4 : H->disable_native_prepares = Z_LVAL_P(val);
657 4 : return 1;
658 : #endif
659 :
660 : default:
661 0 : return 0;
662 : }
663 : }
664 :
665 : static struct pdo_dbh_methods pgsql_methods = {
666 : pgsql_handle_closer,
667 : pgsql_handle_preparer,
668 : pgsql_handle_doer,
669 : pgsql_handle_quoter,
670 : pgsql_handle_begin,
671 : pgsql_handle_commit,
672 : pgsql_handle_rollback,
673 : pdo_pgsql_set_attr,
674 : pdo_pgsql_last_insert_id,
675 : pdo_pgsql_fetch_error_func,
676 : pdo_pgsql_get_attribute,
677 : pdo_pgsql_check_liveness, /* check_liveness */
678 : pdo_pgsql_get_driver_methods /* get_driver_methods */
679 : };
680 :
681 : static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
682 120 : {
683 : pdo_pgsql_db_handle *H;
684 120 : int ret = 0;
685 : char *conn_str, *p, *e;
686 120 : long connect_timeout = 30;
687 :
688 120 : H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
689 120 : dbh->driver_data = H;
690 :
691 120 : H->einfo.errcode = 0;
692 120 : H->einfo.errmsg = NULL;
693 :
694 : /* PostgreSQL wants params in the connect string to be separated by spaces,
695 : * if the PDO standard semicolons are used, we convert them to spaces
696 : */
697 120 : e = (char *) dbh->data_source + strlen(dbh->data_source);
698 120 : p = (char *) dbh->data_source;
699 240 : while ((p = memchr(p, ';', (e - p)))) {
700 0 : *p = ' ';
701 : }
702 :
703 120 : if (driver_options) {
704 3 : connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
705 : }
706 :
707 : /* support both full connection string & connection string + login and/or password */
708 120 : if (dbh->username && dbh->password) {
709 0 : spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
710 120 : } else if (dbh->username) {
711 0 : spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
712 120 : } else if (dbh->password) {
713 0 : spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
714 : } else {
715 120 : spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
716 : }
717 :
718 120 : H->server = PQconnectdb(conn_str);
719 :
720 120 : efree(conn_str);
721 :
722 120 : if (PQstatus(H->server) != CONNECTION_OK) {
723 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
724 0 : goto cleanup;
725 : }
726 :
727 120 : PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
728 :
729 120 : H->attached = 1;
730 120 : H->pgoid = -1;
731 :
732 120 : dbh->methods = &pgsql_methods;
733 120 : dbh->alloc_own_columns = 1;
734 120 : dbh->max_escaped_char_length = 2;
735 :
736 120 : ret = 1;
737 :
738 120 : cleanup:
739 120 : dbh->methods = &pgsql_methods;
740 120 : if (!ret) {
741 0 : pgsql_handle_closer(dbh TSRMLS_CC);
742 : }
743 :
744 120 : return ret;
745 : }
746 : /* }}} */
747 :
748 : pdo_driver_t pdo_pgsql_driver = {
749 : PDO_DRIVER_HEADER(pgsql),
750 : pdo_pgsql_handle_factory
751 : };
752 :
753 : /*
754 : * Local variables:
755 : * tab-width: 4
756 : * c-basic-offset: 4
757 : * End:
758 : * vim600: noet sw=4 ts=4 fdm=marker
759 : * vim<600: noet sw=4 ts=4
760 : */
|