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