xref: /PHP-7.4/ext/pdo_pgsql/pgsql_driver.c (revision 44ade0e8)
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