1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/php_string.h"
29 #include "main/php_network.h"
30 #include "pdo/php_pdo.h"
31 #include "pdo/php_pdo_driver.h"
32 #include "pdo/php_pdo_error.h"
33 #include "ext/standard/file.h"
34 #undef SIZEOF_OFF_T
35 #include "pg_config.h" /* needed for PG_VERSION */
36 #include "php_pdo_pgsql.h"
37 #include "php_pdo_pgsql_int.h"
38 #include "zend_exceptions.h"
39
_pdo_pgsql_trim_message(const char * message,int persistent)40 static char * _pdo_pgsql_trim_message(const char *message, int persistent)
41 {
42 register int i = strlen(message)-1;
43 char *tmp;
44
45 if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
46 --i;
47 }
48 while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
49 --i;
50 }
51 ++i;
52 tmp = pemalloc(i + 1, persistent);
53 memcpy(tmp, message, i);
54 tmp[i] = '\0';
55
56 return tmp;
57 }
58
_pdo_pgsql_escape_credentials(char * str)59 static zend_string* _pdo_pgsql_escape_credentials(char *str)
60 {
61 if (str) {
62 return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'"));
63 }
64
65 return NULL;
66 }
67
_pdo_pgsql_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt,int errcode,const char * sqlstate,const char * msg,const char * file,int line)68 int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */
69 {
70 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
71 pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
72 pdo_pgsql_error_info *einfo = &H->einfo;
73 char *errmsg = PQerrorMessage(H->server);
74
75 einfo->errcode = errcode;
76 einfo->file = file;
77 einfo->line = line;
78
79 if (einfo->errmsg) {
80 pefree(einfo->errmsg, dbh->is_persistent);
81 einfo->errmsg = NULL;
82 }
83
84 if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {
85 strcpy(*pdo_err, "HY000");
86 }
87 else {
88 strcpy(*pdo_err, sqlstate);
89 }
90
91 if (msg) {
92 einfo->errmsg = estrdup(msg);
93 }
94 else if (errmsg) {
95 einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
96 }
97
98 if (!dbh->methods) {
99 pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);
100 }
101
102 return errcode;
103 }
104 /* }}} */
105
_pdo_pgsql_notice(pdo_dbh_t * dbh,const char * message)106 static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
107 {
108 /* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
109 }
110 /* }}} */
111
pdo_pgsql_fetch_error_func(pdo_dbh_t * dbh,pdo_stmt_t * stmt,zval * info)112 static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
113 {
114 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
115 pdo_pgsql_error_info *einfo = &H->einfo;
116
117 if (einfo->errcode) {
118 add_next_index_long(info, einfo->errcode);
119 add_next_index_string(info, einfo->errmsg);
120 }
121
122 return 1;
123 }
124 /* }}} */
125
126 /* {{{ pdo_pgsql_create_lob_stream */
pgsql_lob_write(php_stream * stream,const char * buf,size_t count)127 static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)
128 {
129 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
130 return lo_write(self->conn, self->lfd, (char*)buf, count);
131 }
132
pgsql_lob_read(php_stream * stream,char * buf,size_t count)133 static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)
134 {
135 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
136 return lo_read(self->conn, self->lfd, buf, count);
137 }
138
pgsql_lob_close(php_stream * stream,int close_handle)139 static int pgsql_lob_close(php_stream *stream, int close_handle)
140 {
141 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
142
143 if (close_handle) {
144 lo_close(self->conn, self->lfd);
145 }
146 zval_ptr_dtor(&self->dbh);
147 efree(self);
148 return 0;
149 }
150
pgsql_lob_flush(php_stream * stream)151 static int pgsql_lob_flush(php_stream *stream)
152 {
153 return 0;
154 }
155
pgsql_lob_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)156 static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,
157 zend_off_t *newoffset)
158 {
159 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
160 #if HAVE_PG_LO64 && ZEND_ENABLE_ZVAL_LONG64
161 zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);
162 #else
163 zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);
164 #endif
165 *newoffset = pos;
166 return pos >= 0 ? 0 : -1;
167 }
168
169 const php_stream_ops pdo_pgsql_lob_stream_ops = {
170 pgsql_lob_write,
171 pgsql_lob_read,
172 pgsql_lob_close,
173 pgsql_lob_flush,
174 "pdo_pgsql lob stream",
175 pgsql_lob_seek,
176 NULL,
177 NULL,
178 NULL
179 };
180
pdo_pgsql_create_lob_stream(zval * dbh,int lfd,Oid oid)181 php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid)
182 {
183 php_stream *stm;
184 struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
185 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data;
186
187 ZVAL_COPY_VALUE(&self->dbh, dbh);
188 self->lfd = lfd;
189 self->oid = oid;
190 self->conn = H->server;
191
192 stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
193
194 if (stm) {
195 Z_ADDREF_P(dbh);
196 return stm;
197 }
198
199 efree(self);
200 return NULL;
201 }
202 /* }}} */
203
pgsql_handle_closer(pdo_dbh_t * dbh)204 static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
205 {
206 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
207 if (H) {
208 if (H->server) {
209 PQfinish(H->server);
210 H->server = NULL;
211 }
212 if (H->einfo.errmsg) {
213 pefree(H->einfo.errmsg, dbh->is_persistent);
214 H->einfo.errmsg = NULL;
215 }
216 pefree(H, dbh->is_persistent);
217 dbh->driver_data = NULL;
218 }
219 return 0;
220 }
221 /* }}} */
222
pgsql_handle_preparer(pdo_dbh_t * dbh,const char * sql,size_t sql_len,pdo_stmt_t * stmt,zval * driver_options)223 static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options)
224 {
225 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
226 pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
227 int scrollable;
228 int ret;
229 char *nsql = NULL;
230 size_t nsql_len = 0;
231 int emulate = 0;
232 int execute_only = 0;
233
234 S->H = H;
235 stmt->driver_data = S;
236 stmt->methods = &pgsql_stmt_methods;
237
238 scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
239 PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;
240
241 if (scrollable) {
242 if (S->cursor_name) {
243 efree(S->cursor_name);
244 }
245 spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
246 emulate = 1;
247 } else if (driver_options) {
248 if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {
249 emulate = 1;
250 }
251 if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {
252 execute_only = 1;
253 }
254 } else {
255 emulate = H->disable_native_prepares || H->emulate_prepares;
256 execute_only = H->disable_prepares;
257 }
258
259 if (!emulate && PQprotocolVersion(H->server) <= 2) {
260 emulate = 1;
261 }
262
263 if (emulate) {
264 stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
265 } else {
266 stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
267 stmt->named_rewrite_template = "$%d";
268 }
269
270 ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len);
271
272 if (ret == -1) {
273 /* couldn't grok it */
274 strcpy(dbh->error_code, stmt->error_code);
275 return 0;
276 } else if (ret == 1) {
277 /* query was re-written */
278 S->query = nsql;
279 } else {
280 S->query = estrdup(sql);
281 }
282
283 if (!emulate && !execute_only) {
284 /* prepared query: set the query name and defer the
285 actual prepare until the first execute call */
286 spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
287 }
288
289 return 1;
290 }
291
pgsql_handle_doer(pdo_dbh_t * dbh,const char * sql,size_t sql_len)292 static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len)
293 {
294 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
295 PGresult *res;
296 zend_long ret = 1;
297 ExecStatusType qs;
298
299 if (!(res = PQexec(H->server, sql))) {
300 /* fatal error */
301 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
302 return -1;
303 }
304 qs = PQresultStatus(res);
305 if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
306 pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
307 PQclear(res);
308 return -1;
309 }
310 H->pgoid = PQoidValue(res);
311 if (qs == PGRES_COMMAND_OK) {
312 ZEND_ATOL(ret, PQcmdTuples(res));
313 } else {
314 ret = Z_L(0);
315 }
316 PQclear(res);
317
318 return ret;
319 }
320
pgsql_handle_quoter(pdo_dbh_t * dbh,const char * unquoted,size_t unquotedlen,char ** quoted,size_t * quotedlen,enum pdo_param_type paramtype)321 static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype)
322 {
323 unsigned char *escaped;
324 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
325 size_t tmp_len;
326
327 switch (paramtype) {
328 case PDO_PARAM_LOB:
329 /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
330 escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len);
331 *quotedlen = tmp_len + 1;
332 *quoted = emalloc(*quotedlen + 1);
333 memcpy((*quoted)+1, escaped, *quotedlen-2);
334 (*quoted)[0] = '\'';
335 (*quoted)[*quotedlen-1] = '\'';
336 (*quoted)[*quotedlen] = '\0';
337 PQfreemem(escaped);
338 break;
339 default:
340 *quoted = safe_emalloc(2, unquotedlen, 3);
341 (*quoted)[0] = '\'';
342 *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
343 (*quoted)[*quotedlen + 1] = '\'';
344 (*quoted)[*quotedlen + 2] = '\0';
345 *quotedlen += 2;
346 }
347 return 1;
348 }
349
pdo_pgsql_last_insert_id(pdo_dbh_t * dbh,const char * name,size_t * len)350 static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len)
351 {
352 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
353 char *id = NULL;
354 PGresult *res;
355 ExecStatusType status;
356
357 if (name == NULL) {
358 res = PQexec(H->server, "SELECT LASTVAL()");
359 } else {
360 const char *q[1];
361 q[0] = name;
362
363 res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
364 }
365 status = PQresultStatus(res);
366
367 if (res && (status == PGRES_TUPLES_OK)) {
368 id = estrdup((char *)PQgetvalue(res, 0, 0));
369 *len = PQgetlength(res, 0, 0);
370 } else {
371 pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
372 }
373
374 if (res) {
375 PQclear(res);
376 }
377
378 return id;
379 }
380
pdo_pgsql_get_attribute(pdo_dbh_t * dbh,zend_long attr,zval * return_value)381 static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
382 {
383 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
384
385 switch (attr) {
386 case PDO_ATTR_EMULATE_PREPARES:
387 ZVAL_BOOL(return_value, H->emulate_prepares);
388 break;
389
390 case PDO_PGSQL_ATTR_DISABLE_PREPARES:
391 ZVAL_BOOL(return_value, H->disable_prepares);
392 break;
393
394 case PDO_ATTR_CLIENT_VERSION:
395 ZVAL_STRING(return_value, PG_VERSION);
396 break;
397
398 case PDO_ATTR_SERVER_VERSION:
399 if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
400 ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"));
401 } else /* emulate above via a query */
402 {
403 PGresult *res = PQexec(H->server, "SELECT VERSION()");
404 if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
405 ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));
406 }
407
408 if (res) {
409 PQclear(res);
410 }
411 }
412 break;
413
414 case PDO_ATTR_CONNECTION_STATUS:
415 switch (PQstatus(H->server)) {
416 case CONNECTION_STARTED:
417 ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1);
418 break;
419
420 case CONNECTION_MADE:
421 case CONNECTION_OK:
422 ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1);
423 break;
424
425 case CONNECTION_AWAITING_RESPONSE:
426 ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1);
427 break;
428
429 case CONNECTION_AUTH_OK:
430 ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1);
431 break;
432 #ifdef CONNECTION_SSL_STARTUP
433 case CONNECTION_SSL_STARTUP:
434 ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1);
435 break;
436 #endif
437 case CONNECTION_SETENV:
438 ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1);
439 break;
440
441 case CONNECTION_BAD:
442 default:
443 ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1);
444 break;
445 }
446 break;
447
448 case PDO_ATTR_SERVER_INFO: {
449 int spid = PQbackendPID(H->server);
450
451
452 zend_string *str_info =
453 strpprintf(0,
454 "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
455 spid,
456 (char*)PQparameterStatus(H->server, "client_encoding"),
457 (char*)PQparameterStatus(H->server, "is_superuser"),
458 (char*)PQparameterStatus(H->server, "session_authorization"),
459 (char*)PQparameterStatus(H->server, "DateStyle"));
460
461 ZVAL_STR(return_value, str_info);
462 }
463 break;
464
465 default:
466 return 0;
467 }
468
469 return 1;
470 }
471
472 /* {{{ */
pdo_pgsql_check_liveness(pdo_dbh_t * dbh)473 static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh)
474 {
475 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
476 if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {
477 PQreset(H->server);
478 }
479 return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
480 }
481 /* }}} */
482
pgsql_handle_in_transaction(pdo_dbh_t * dbh)483 static int pgsql_handle_in_transaction(pdo_dbh_t *dbh)
484 {
485 pdo_pgsql_db_handle *H;
486
487 H = (pdo_pgsql_db_handle *)dbh->driver_data;
488
489 return PQtransactionStatus(H->server) > PQTRANS_IDLE;
490 }
491
pdo_pgsql_transaction_cmd(const char * cmd,pdo_dbh_t * dbh)492 static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)
493 {
494 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
495 PGresult *res;
496 int ret = 1;
497
498 res = PQexec(H->server, cmd);
499
500 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
501 pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
502 ret = 0;
503 }
504
505 PQclear(res);
506 return ret;
507 }
508
pgsql_handle_begin(pdo_dbh_t * dbh)509 static int pgsql_handle_begin(pdo_dbh_t *dbh)
510 {
511 return pdo_pgsql_transaction_cmd("BEGIN", dbh);
512 }
513
pgsql_handle_commit(pdo_dbh_t * dbh)514 static int pgsql_handle_commit(pdo_dbh_t *dbh)
515 {
516 int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh);
517
518 /* When deferred constraints are used the commit could
519 fail, and a ROLLBACK implicitly ran. See bug #67462 */
520 if (!ret) {
521 dbh->in_txn = pgsql_handle_in_transaction(dbh);
522 }
523
524 return ret;
525 }
526
pgsql_handle_rollback(pdo_dbh_t * dbh)527 static int pgsql_handle_rollback(pdo_dbh_t *dbh)
528 {
529 return pdo_pgsql_transaction_cmd("ROLLBACK", dbh);
530 }
531
532 /* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
533 Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO,pgsqlCopyFromArray)534 static PHP_METHOD(PDO, pgsqlCopyFromArray)
535 {
536 pdo_dbh_t *dbh;
537 pdo_pgsql_db_handle *H;
538
539 zval *pg_rows;
540
541 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
542 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
543 char *query;
544
545 PGresult *pgsql_result;
546 ExecStatusType status;
547
548 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss",
549 &table_name, &table_name_len, &pg_rows,
550 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
551 return;
552 }
553
554 if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
555 php_error_docref(NULL, E_WARNING, "Cannot copy from an empty array");
556 RETURN_FALSE;
557 }
558
559 dbh = Z_PDO_DBH_P(ZEND_THIS);
560 PDO_CONSTRUCT_CHECK;
561 PDO_DBH_CLEAR_ERR();
562
563 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
564 if (pg_fields) {
565 spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
566 } else {
567 spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
568 }
569
570 /* Obtain db Handle */
571 H = (pdo_pgsql_db_handle *)dbh->driver_data;
572
573 while ((pgsql_result = PQgetResult(H->server))) {
574 PQclear(pgsql_result);
575 }
576 pgsql_result = PQexec(H->server, query);
577
578 efree(query);
579 query = NULL;
580
581 if (pgsql_result) {
582 status = PQresultStatus(pgsql_result);
583 } else {
584 status = (ExecStatusType) PQstatus(H->server);
585 }
586
587 if (status == PGRES_COPY_IN && pgsql_result) {
588 int command_failed = 0;
589 size_t buffer_len = 0;
590 zval *tmp;
591
592 PQclear(pgsql_result);
593 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
594 size_t query_len;
595 if (!try_convert_to_string(tmp)) {
596 efree(query);
597 return;
598 }
599
600 if (buffer_len < Z_STRLEN_P(tmp)) {
601 buffer_len = Z_STRLEN_P(tmp);
602 query = erealloc(query, buffer_len + 2); /* room for \n\0 */
603 }
604 memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
605 query_len = Z_STRLEN_P(tmp);
606 if (query[query_len - 1] != '\n') {
607 query[query_len++] = '\n';
608 }
609 query[query_len] = '\0';
610 if (PQputCopyData(H->server, query, query_len) != 1) {
611 efree(query);
612 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
613 PDO_HANDLE_DBH_ERR();
614 RETURN_FALSE;
615 }
616 } ZEND_HASH_FOREACH_END();
617 if (query) {
618 efree(query);
619 }
620
621 if (PQputCopyEnd(H->server, NULL) != 1) {
622 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
623 PDO_HANDLE_DBH_ERR();
624 RETURN_FALSE;
625 }
626
627 while ((pgsql_result = PQgetResult(H->server))) {
628 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
629 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
630 command_failed = 1;
631 }
632 PQclear(pgsql_result);
633 }
634
635 PDO_HANDLE_DBH_ERR();
636 RETURN_BOOL(!command_failed);
637 } else {
638 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
639 PQclear(pgsql_result);
640 PDO_HANDLE_DBH_ERR();
641 RETURN_FALSE;
642 }
643 }
644 /* }}} */
645
646 /* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
647 Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO,pgsqlCopyFromFile)648 static PHP_METHOD(PDO, pgsqlCopyFromFile)
649 {
650 pdo_dbh_t *dbh;
651 pdo_pgsql_db_handle *H;
652
653 char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
654 size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
655 char *query;
656 PGresult *pgsql_result;
657 ExecStatusType status;
658 php_stream *stream;
659
660 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss",
661 &table_name, &table_name_len, &filename, &filename_len,
662 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
663 return;
664 }
665
666 /* Obtain db Handler */
667 dbh = Z_PDO_DBH_P(ZEND_THIS);
668 PDO_CONSTRUCT_CHECK;
669 PDO_DBH_CLEAR_ERR();
670
671 stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context));
672 if (!stream) {
673 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
674 PDO_HANDLE_DBH_ERR();
675 RETURN_FALSE;
676 }
677
678 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
679 if (pg_fields) {
680 spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
681 } else {
682 spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
683 }
684
685 H = (pdo_pgsql_db_handle *)dbh->driver_data;
686
687 while ((pgsql_result = PQgetResult(H->server))) {
688 PQclear(pgsql_result);
689 }
690 pgsql_result = PQexec(H->server, query);
691
692 efree(query);
693
694 if (pgsql_result) {
695 status = PQresultStatus(pgsql_result);
696 } else {
697 status = (ExecStatusType) PQstatus(H->server);
698 }
699
700 if (status == PGRES_COPY_IN && pgsql_result) {
701 char *buf;
702 int command_failed = 0;
703 size_t line_len = 0;
704
705 PQclear(pgsql_result);
706 while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
707 if (PQputCopyData(H->server, buf, line_len) != 1) {
708 efree(buf);
709 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
710 php_stream_close(stream);
711 PDO_HANDLE_DBH_ERR();
712 RETURN_FALSE;
713 }
714 efree(buf);
715 }
716 php_stream_close(stream);
717
718 if (PQputCopyEnd(H->server, NULL) != 1) {
719 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
720 PDO_HANDLE_DBH_ERR();
721 RETURN_FALSE;
722 }
723
724 while ((pgsql_result = PQgetResult(H->server))) {
725 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
726 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
727 command_failed = 1;
728 }
729 PQclear(pgsql_result);
730 }
731
732 PDO_HANDLE_DBH_ERR();
733 RETURN_BOOL(!command_failed);
734 } else {
735 php_stream_close(stream);
736 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
737 PQclear(pgsql_result);
738 PDO_HANDLE_DBH_ERR();
739 RETURN_FALSE;
740 }
741 }
742 /* }}} */
743
744
745 /* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , string $filename, [string $delimiter [, string $null_as [, string $fields]]])
746 Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO,pgsqlCopyToFile)747 static PHP_METHOD(PDO, pgsqlCopyToFile)
748 {
749 pdo_dbh_t *dbh;
750 pdo_pgsql_db_handle *H;
751
752 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
753 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
754 char *query;
755
756 PGresult *pgsql_result;
757 ExecStatusType status;
758
759 php_stream *stream;
760
761 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss",
762 &table_name, &table_name_len, &filename, &filename_len,
763 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
764 return;
765 }
766
767 dbh = Z_PDO_DBH_P(ZEND_THIS);
768 PDO_CONSTRUCT_CHECK;
769 PDO_DBH_CLEAR_ERR();
770
771 H = (pdo_pgsql_db_handle *)dbh->driver_data;
772
773 stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context));
774 if (!stream) {
775 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
776 PDO_HANDLE_DBH_ERR();
777 RETURN_FALSE;
778 }
779
780 while ((pgsql_result = PQgetResult(H->server))) {
781 PQclear(pgsql_result);
782 }
783
784 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
785 if (pg_fields) {
786 spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
787 } else {
788 spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
789 }
790 pgsql_result = PQexec(H->server, query);
791 efree(query);
792
793 if (pgsql_result) {
794 status = PQresultStatus(pgsql_result);
795 } else {
796 status = (ExecStatusType) PQstatus(H->server);
797 }
798
799 if (status == PGRES_COPY_OUT && pgsql_result) {
800 PQclear(pgsql_result);
801 while (1) {
802 char *csv = NULL;
803 int ret = PQgetCopyData(H->server, &csv, 0);
804
805 if (ret == -1) {
806 break; /* done */
807 } else if (ret > 0) {
808 if (php_stream_write(stream, csv, ret) != (size_t)ret) {
809 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
810 PQfreemem(csv);
811 php_stream_close(stream);
812 PDO_HANDLE_DBH_ERR();
813 RETURN_FALSE;
814 } else {
815 PQfreemem(csv);
816 }
817 } else {
818 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
819 php_stream_close(stream);
820 PDO_HANDLE_DBH_ERR();
821 RETURN_FALSE;
822 }
823 }
824 php_stream_close(stream);
825
826 while ((pgsql_result = PQgetResult(H->server))) {
827 PQclear(pgsql_result);
828 }
829 RETURN_TRUE;
830 } else {
831 php_stream_close(stream);
832 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
833 PQclear(pgsql_result);
834 PDO_HANDLE_DBH_ERR();
835 RETURN_FALSE;
836 }
837 }
838 /* }}} */
839
840 /* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
841 Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO,pgsqlCopyToArray)842 static PHP_METHOD(PDO, pgsqlCopyToArray)
843 {
844 pdo_dbh_t *dbh;
845 pdo_pgsql_db_handle *H;
846
847 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
848 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
849 char *query;
850
851 PGresult *pgsql_result;
852 ExecStatusType status;
853
854 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss",
855 &table_name, &table_name_len,
856 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
857 return;
858 }
859
860 dbh = Z_PDO_DBH_P(ZEND_THIS);
861 PDO_CONSTRUCT_CHECK;
862 PDO_DBH_CLEAR_ERR();
863
864 H = (pdo_pgsql_db_handle *)dbh->driver_data;
865
866 while ((pgsql_result = PQgetResult(H->server))) {
867 PQclear(pgsql_result);
868 }
869
870 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
871 if (pg_fields) {
872 spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
873 } else {
874 spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
875 }
876 pgsql_result = PQexec(H->server, query);
877 efree(query);
878
879 if (pgsql_result) {
880 status = PQresultStatus(pgsql_result);
881 } else {
882 status = (ExecStatusType) PQstatus(H->server);
883 }
884
885 if (status == PGRES_COPY_OUT && pgsql_result) {
886 PQclear(pgsql_result);
887 array_init(return_value);
888
889 while (1) {
890 char *csv = NULL;
891 int ret = PQgetCopyData(H->server, &csv, 0);
892 if (ret == -1) {
893 break; /* copy done */
894 } else if (ret > 0) {
895 add_next_index_stringl(return_value, csv, ret);
896 PQfreemem(csv);
897 } else {
898 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
899 PDO_HANDLE_DBH_ERR();
900 RETURN_FALSE;
901 }
902 }
903
904 while ((pgsql_result = PQgetResult(H->server))) {
905 PQclear(pgsql_result);
906 }
907 } else {
908 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
909 PQclear(pgsql_result);
910 PDO_HANDLE_DBH_ERR();
911 RETURN_FALSE;
912 }
913 }
914 /* }}} */
915
916
917 /* {{{ proto string PDO::pgsqlLOBCreate()
918 Creates a new large object, returning its identifier. Must be called inside a transaction. */
PHP_METHOD(PDO,pgsqlLOBCreate)919 static PHP_METHOD(PDO, pgsqlLOBCreate)
920 {
921 pdo_dbh_t *dbh;
922 pdo_pgsql_db_handle *H;
923 Oid lfd;
924
925 dbh = Z_PDO_DBH_P(ZEND_THIS);
926 PDO_CONSTRUCT_CHECK;
927 PDO_DBH_CLEAR_ERR();
928
929 H = (pdo_pgsql_db_handle *)dbh->driver_data;
930 lfd = lo_creat(H->server, INV_READ|INV_WRITE);
931
932 if (lfd != InvalidOid) {
933 zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);
934
935 RETURN_STR(buf);
936 }
937
938 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
939 PDO_HANDLE_DBH_ERR();
940 RETURN_FALSE;
941 }
942 /* }}} */
943
944 /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
945 Opens an existing large object stream. Must be called inside a transaction. */
PHP_METHOD(PDO,pgsqlLOBOpen)946 static PHP_METHOD(PDO, pgsqlLOBOpen)
947 {
948 pdo_dbh_t *dbh;
949 pdo_pgsql_db_handle *H;
950 Oid oid;
951 int lfd;
952 char *oidstr;
953 size_t oidstrlen;
954 char *modestr = "rb";
955 size_t modestrlen;
956 int mode = INV_READ;
957 char *end_ptr;
958
959 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s",
960 &oidstr, &oidstrlen, &modestr, &modestrlen)) {
961 RETURN_FALSE;
962 }
963
964 oid = (Oid)strtoul(oidstr, &end_ptr, 10);
965 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
966 RETURN_FALSE;
967 }
968
969 if (strpbrk(modestr, "+w")) {
970 mode = INV_READ|INV_WRITE;
971 }
972
973 dbh = Z_PDO_DBH_P(ZEND_THIS);
974 PDO_CONSTRUCT_CHECK;
975 PDO_DBH_CLEAR_ERR();
976
977 H = (pdo_pgsql_db_handle *)dbh->driver_data;
978
979 lfd = lo_open(H->server, oid, mode);
980
981 if (lfd >= 0) {
982 php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid);
983 if (stream) {
984 php_stream_to_zval(stream, return_value);
985 return;
986 }
987 } else {
988 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
989 }
990
991 PDO_HANDLE_DBH_ERR();
992 RETURN_FALSE;
993 }
994 /* }}} */
995
996 /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
997 Deletes the large object identified by oid. Must be called inside a transaction. */
PHP_METHOD(PDO,pgsqlLOBUnlink)998 static PHP_METHOD(PDO, pgsqlLOBUnlink)
999 {
1000 pdo_dbh_t *dbh;
1001 pdo_pgsql_db_handle *H;
1002 Oid oid;
1003 char *oidstr, *end_ptr;
1004 size_t oidlen;
1005
1006 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s",
1007 &oidstr, &oidlen)) {
1008 RETURN_FALSE;
1009 }
1010
1011 oid = (Oid)strtoul(oidstr, &end_ptr, 10);
1012 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
1013 RETURN_FALSE;
1014 }
1015
1016 dbh = Z_PDO_DBH_P(ZEND_THIS);
1017 PDO_CONSTRUCT_CHECK;
1018 PDO_DBH_CLEAR_ERR();
1019
1020 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1021
1022 if (1 == lo_unlink(H->server, oid)) {
1023 RETURN_TRUE;
1024 }
1025
1026 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1027 PDO_HANDLE_DBH_ERR();
1028 RETURN_FALSE;
1029 }
1030 /* }}} */
1031
1032 /* {{{ proto mixed PDO::pgsqlGetNotify([ int $result_type = PDO::FETCH_USE_DEFAULT] [, int $ms_timeout = 0 ]])
1033 Get asyncronous notification */
PHP_METHOD(PDO,pgsqlGetNotify)1034 static PHP_METHOD(PDO, pgsqlGetNotify)
1035 {
1036 pdo_dbh_t *dbh;
1037 pdo_pgsql_db_handle *H;
1038 zend_long result_type = PDO_FETCH_USE_DEFAULT;
1039 zend_long ms_timeout = 0;
1040 PGnotify *pgsql_notify;
1041
1042 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll",
1043 &result_type, &ms_timeout)) {
1044 RETURN_FALSE;
1045 }
1046
1047 dbh = Z_PDO_DBH_P(ZEND_THIS);
1048 PDO_CONSTRUCT_CHECK;
1049
1050 if (result_type == PDO_FETCH_USE_DEFAULT) {
1051 result_type = dbh->default_fetch_type;
1052 }
1053
1054 if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
1055 php_error_docref(NULL, E_WARNING, "Invalid result type");
1056 RETURN_FALSE;
1057 }
1058
1059 if (ms_timeout < 0) {
1060 php_error_docref(NULL, E_WARNING, "Invalid timeout");
1061 RETURN_FALSE;
1062 #if ZEND_ENABLE_ZVAL_LONG64
1063 } else if (ms_timeout > INT_MAX) {
1064 php_error_docref(NULL, E_WARNING, "timeout was shrunk to %d", INT_MAX);
1065 ms_timeout = INT_MAX;
1066 #endif
1067 }
1068
1069 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1070
1071 if (!PQconsumeInput(H->server)) {
1072 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1073 PDO_HANDLE_DBH_ERR();
1074 RETURN_FALSE;
1075 }
1076 pgsql_notify = PQnotifies(H->server);
1077
1078 if (ms_timeout && !pgsql_notify) {
1079 php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);
1080
1081 if (!PQconsumeInput(H->server)) {
1082 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1083 PDO_HANDLE_DBH_ERR();
1084 RETURN_FALSE;
1085 }
1086 pgsql_notify = PQnotifies(H->server);
1087 }
1088
1089 if (!pgsql_notify) {
1090 RETURN_FALSE;
1091 }
1092
1093 array_init(return_value);
1094 if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {
1095 add_index_string(return_value, 0, pgsql_notify->relname);
1096 add_index_long(return_value, 1, pgsql_notify->be_pid);
1097 if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1098 add_index_string(return_value, 2, pgsql_notify->extra);
1099 }
1100 }
1101 if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {
1102 add_assoc_string(return_value, "message", pgsql_notify->relname);
1103 add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
1104 if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1105 add_assoc_string(return_value, "payload", pgsql_notify->extra);
1106 }
1107 }
1108
1109 PQfreemem(pgsql_notify);
1110 }
1111 /* }}} */
1112
1113 /* {{{ proto int PDO::pgsqlGetPid()
1114 Get backend(server) pid */
PHP_METHOD(PDO,pgsqlGetPid)1115 static PHP_METHOD(PDO, pgsqlGetPid)
1116 {
1117 pdo_dbh_t *dbh;
1118 pdo_pgsql_db_handle *H;
1119
1120 dbh = Z_PDO_DBH_P(ZEND_THIS);
1121 PDO_CONSTRUCT_CHECK;
1122
1123 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1124
1125 RETURN_LONG(PQbackendPID(H->server));
1126 }
1127 /* }}} */
1128
1129
1130 static const zend_function_entry dbh_methods[] = {
1131 PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
1132 PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
1133 PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
1134 PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
1135 PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
1136 PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
1137 PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
1138 PHP_ME(PDO, pgsqlGetNotify, NULL, ZEND_ACC_PUBLIC)
1139 PHP_ME(PDO, pgsqlGetPid, NULL, ZEND_ACC_PUBLIC)
1140 PHP_FE_END
1141 };
1142
pdo_pgsql_get_driver_methods(pdo_dbh_t * dbh,int kind)1143 static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)
1144 {
1145 switch (kind) {
1146 case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1147 return dbh_methods;
1148 default:
1149 return NULL;
1150 }
1151 }
1152
pdo_pgsql_set_attr(pdo_dbh_t * dbh,zend_long attr,zval * val)1153 static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
1154 {
1155 zend_bool bval = zval_get_long(val)? 1 : 0;
1156 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
1157
1158 switch (attr) {
1159 case PDO_ATTR_EMULATE_PREPARES:
1160 H->emulate_prepares = bval;
1161 return 1;
1162 case PDO_PGSQL_ATTR_DISABLE_PREPARES:
1163 H->disable_prepares = bval;
1164 return 1;
1165 default:
1166 return 0;
1167 }
1168 }
1169
1170 static const struct pdo_dbh_methods pgsql_methods = {
1171 pgsql_handle_closer,
1172 pgsql_handle_preparer,
1173 pgsql_handle_doer,
1174 pgsql_handle_quoter,
1175 pgsql_handle_begin,
1176 pgsql_handle_commit,
1177 pgsql_handle_rollback,
1178 pdo_pgsql_set_attr,
1179 pdo_pgsql_last_insert_id,
1180 pdo_pgsql_fetch_error_func,
1181 pdo_pgsql_get_attribute,
1182 pdo_pgsql_check_liveness, /* check_liveness */
1183 pdo_pgsql_get_driver_methods, /* get_driver_methods */
1184 NULL,
1185 pgsql_handle_in_transaction,
1186 };
1187
pdo_pgsql_handle_factory(pdo_dbh_t * dbh,zval * driver_options)1188 static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
1189 {
1190 pdo_pgsql_db_handle *H;
1191 int ret = 0;
1192 char *conn_str, *p, *e;
1193 zend_string *tmp_user, *tmp_pass;
1194 zend_long connect_timeout = 30;
1195
1196 H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1197 dbh->driver_data = H;
1198
1199 dbh->skip_param_evt =
1200 1 << PDO_PARAM_EVT_EXEC_POST |
1201 1 << PDO_PARAM_EVT_FETCH_PRE |
1202 1 << PDO_PARAM_EVT_FETCH_POST;
1203
1204 H->einfo.errcode = 0;
1205 H->einfo.errmsg = NULL;
1206
1207 /* PostgreSQL wants params in the connect string to be separated by spaces,
1208 * if the PDO standard semicolons are used, we convert them to spaces
1209 */
1210 e = (char *) dbh->data_source + strlen(dbh->data_source);
1211 p = (char *) dbh->data_source;
1212 while ((p = memchr(p, ';', (e - p)))) {
1213 *p = ' ';
1214 }
1215
1216 if (driver_options) {
1217 connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
1218 }
1219
1220 /* escape username and password, if provided */
1221 tmp_user = _pdo_pgsql_escape_credentials(dbh->username);
1222 tmp_pass = _pdo_pgsql_escape_credentials(dbh->password);
1223
1224 /* support both full connection string & connection string + login and/or password */
1225 if (tmp_user && tmp_pass) {
1226 spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);
1227 } else if (tmp_user) {
1228 spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);
1229 } else if (tmp_pass) {
1230 spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);
1231 } else {
1232 spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout);
1233 }
1234
1235 H->server = PQconnectdb(conn_str);
1236
1237 if (tmp_user) {
1238 zend_string_release_ex(tmp_user, 0);
1239 }
1240 if (tmp_pass) {
1241 zend_string_release_ex(tmp_pass, 0);
1242 }
1243
1244 efree(conn_str);
1245
1246 if (PQstatus(H->server) != CONNECTION_OK) {
1247 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1248 goto cleanup;
1249 }
1250
1251 PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1252
1253 H->attached = 1;
1254 H->pgoid = -1;
1255
1256 dbh->methods = &pgsql_methods;
1257 dbh->alloc_own_columns = 1;
1258 dbh->max_escaped_char_length = 2;
1259
1260 ret = 1;
1261
1262 cleanup:
1263 dbh->methods = &pgsql_methods;
1264 if (!ret) {
1265 pgsql_handle_closer(dbh);
1266 }
1267
1268 return ret;
1269 }
1270 /* }}} */
1271
1272 const pdo_driver_t pdo_pgsql_driver = {
1273 PDO_DRIVER_HEADER(pgsql),
1274 pdo_pgsql_handle_factory
1275 };
1276