1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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: Zeev Suraski <zeev@zend.com>                                |
16   |          Jouni Ahto <jouni.ahto@exdec.fi>                            |
17   |          Yasuo Ohgaki <yohgaki@php.net>                              |
18   |          Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*)         |
19   |          Chris Kings-Lynne <chriskl@php.net> (v3 protocol)           |
20   +----------------------------------------------------------------------+
21 */
22
23/* $Id$ */
24
25#include <stdlib.h>
26
27#define PHP_PGSQL_PRIVATE 1
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#define SMART_STR_PREALLOC 512
34
35#include "php.h"
36#include "php_ini.h"
37#include "ext/standard/php_standard.h"
38#include "zend_smart_str.h"
39#include "ext/pcre/php_pcre.h"
40#ifdef PHP_WIN32
41# include "win32/time.h"
42#endif
43
44#undef PACKAGE_BUGREPORT
45#undef PACKAGE_NAME
46#undef PACKAGE_STRING
47#undef PACKAGE_TARNAME
48#undef PACKAGE_VERSION
49#include "php_pgsql.h"
50#include "php_globals.h"
51#include "zend_exceptions.h"
52
53#if HAVE_PGSQL
54
55#ifndef InvalidOid
56#define InvalidOid ((Oid) 0)
57#endif
58
59#define PGSQL_ASSOC           1<<0
60#define PGSQL_NUM             1<<1
61#define PGSQL_BOTH            (PGSQL_ASSOC|PGSQL_NUM)
62
63#define PGSQL_NOTICE_LAST     1  /* Get the last notice */
64#define PGSQL_NOTICE_ALL      2  /* Get all notices */
65#define PGSQL_NOTICE_CLEAR    3  /* Remove notices */
66
67#define PGSQL_STATUS_LONG     1
68#define PGSQL_STATUS_STRING   2
69
70#define PGSQL_MAX_LENGTH_OF_LONG   30
71#define PGSQL_MAX_LENGTH_OF_DOUBLE 60
72
73#if ZEND_LONG_MAX < UINT_MAX
74#define PGSQL_RETURN_OID(oid) do { \
75	if (oid > ZEND_LONG_MAX) { \
76		smart_str s = {0}; \
77		smart_str_append_unsigned(&s, oid); \
78		smart_str_0(&s); \
79		RETURN_NEW_STR(s.s); \
80	} \
81	RETURN_LONG((zend_long)oid); \
82} while(0)
83#else
84#define PGSQL_RETURN_OID(oid) RETURN_LONG((zend_long)oid)
85#endif
86
87#if HAVE_PQSETNONBLOCKING
88#define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
89#else
90#define PQ_SETNONBLOCKING(pg_link, flag) 0
91#endif
92
93#define CHECK_DEFAULT_LINK(x) if ((x) == NULL) { php_error_docref(NULL, E_WARNING, "No PostgreSQL link opened yet"); }
94#define FETCH_DEFAULT_LINK()  PGG(default_link)
95
96#ifndef HAVE_PQFREEMEM
97#define PQfreemem free
98#endif
99
100ZEND_DECLARE_MODULE_GLOBALS(pgsql)
101static PHP_GINIT_FUNCTION(pgsql);
102
103/* {{{ arginfo */
104ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
105	ZEND_ARG_INFO(0, connection_string)
106	ZEND_ARG_INFO(0, connect_type)
107	ZEND_ARG_INFO(0, host)
108	ZEND_ARG_INFO(0, port)
109	ZEND_ARG_INFO(0, options)
110	ZEND_ARG_INFO(0, tty)
111	ZEND_ARG_INFO(0, database)
112ZEND_END_ARG_INFO()
113
114ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
115	ZEND_ARG_INFO(0, connection_string)
116	ZEND_ARG_INFO(0, host)
117	ZEND_ARG_INFO(0, port)
118	ZEND_ARG_INFO(0, options)
119	ZEND_ARG_INFO(0, tty)
120	ZEND_ARG_INFO(0, database)
121ZEND_END_ARG_INFO()
122
123ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect_poll, 0, 0, 0)
124	ZEND_ARG_INFO(0, connection)
125ZEND_END_ARG_INFO()
126
127#if HAVE_PQPARAMETERSTATUS
128ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1)
129	ZEND_ARG_INFO(0, connection)
130	ZEND_ARG_INFO(0, param_name)
131ZEND_END_ARG_INFO()
132#endif
133
134ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_close, 0, 0, 0)
135	ZEND_ARG_INFO(0, connection)
136ZEND_END_ARG_INFO()
137
138ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_dbname, 0, 0, 0)
139	ZEND_ARG_INFO(0, connection)
140ZEND_END_ARG_INFO()
141
142ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_error, 0, 0, 0)
143	ZEND_ARG_INFO(0, connection)
144ZEND_END_ARG_INFO()
145
146ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_options, 0, 0, 0)
147	ZEND_ARG_INFO(0, connection)
148ZEND_END_ARG_INFO()
149
150ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_port, 0, 0, 0)
151	ZEND_ARG_INFO(0, connection)
152ZEND_END_ARG_INFO()
153
154ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_tty, 0, 0, 0)
155	ZEND_ARG_INFO(0, connection)
156ZEND_END_ARG_INFO()
157
158ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_host, 0, 0, 0)
159	ZEND_ARG_INFO(0, connection)
160ZEND_END_ARG_INFO()
161
162ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_version, 0, 0, 0)
163	ZEND_ARG_INFO(0, connection)
164ZEND_END_ARG_INFO()
165
166ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_ping, 0, 0, 0)
167	ZEND_ARG_INFO(0, connection)
168ZEND_END_ARG_INFO()
169
170ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0)
171	ZEND_ARG_INFO(0, connection)
172	ZEND_ARG_INFO(0, query)
173ZEND_END_ARG_INFO()
174
175#if HAVE_PQEXECPARAMS
176ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query_params, 0, 0, 0)
177	ZEND_ARG_INFO(0, connection)
178	ZEND_ARG_INFO(0, query)
179	ZEND_ARG_INFO(0, params)
180ZEND_END_ARG_INFO()
181#endif
182
183#if HAVE_PQPREPARE
184ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_prepare, 0, 0, 0)
185	ZEND_ARG_INFO(0, connection)
186	ZEND_ARG_INFO(0, stmtname)
187	ZEND_ARG_INFO(0, query)
188ZEND_END_ARG_INFO()
189#endif
190
191#if HAVE_PQEXECPREPARED
192ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_execute, 0, 0, 0)
193	ZEND_ARG_INFO(0, connection)
194	ZEND_ARG_INFO(0, stmtname)
195	ZEND_ARG_INFO(0, params)
196ZEND_END_ARG_INFO()
197#endif
198
199ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 1)
200	ZEND_ARG_INFO(0, result)
201ZEND_END_ARG_INFO()
202
203ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_fields, 0, 0, 1)
204	ZEND_ARG_INFO(0, result)
205ZEND_END_ARG_INFO()
206
207#if HAVE_PQCMDTUPLES
208ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 1)
209	ZEND_ARG_INFO(0, result)
210ZEND_END_ARG_INFO()
211#endif
212
213ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
214	ZEND_ARG_INFO(0, connection)
215	ZEND_ARG_INFO(0, option)
216ZEND_END_ARG_INFO()
217
218#ifdef HAVE_PQFTABLE
219ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_table, 0, 0, 2)
220	ZEND_ARG_INFO(0, result)
221	ZEND_ARG_INFO(0, field_number)
222	ZEND_ARG_INFO(0, oid_only)
223ZEND_END_ARG_INFO()
224#endif
225
226ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_name, 0, 0, 2)
227	ZEND_ARG_INFO(0, result)
228	ZEND_ARG_INFO(0, field_number)
229ZEND_END_ARG_INFO()
230
231ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_size, 0, 0, 2)
232	ZEND_ARG_INFO(0, result)
233	ZEND_ARG_INFO(0, field_number)
234ZEND_END_ARG_INFO()
235
236ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type, 0, 0, 2)
237	ZEND_ARG_INFO(0, result)
238	ZEND_ARG_INFO(0, field_number)
239ZEND_END_ARG_INFO()
240
241ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type_oid, 0, 0, 2)
242	ZEND_ARG_INFO(0, result)
243	ZEND_ARG_INFO(0, field_number)
244ZEND_END_ARG_INFO()
245
246ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_num, 0, 0, 2)
247	ZEND_ARG_INFO(0, result)
248	ZEND_ARG_INFO(0, field_name)
249ZEND_END_ARG_INFO()
250
251ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_result, 0, 0, 1)
252	ZEND_ARG_INFO(0, result)
253	ZEND_ARG_INFO(0, row_number)
254	ZEND_ARG_INFO(0, field_name)
255ZEND_END_ARG_INFO()
256
257ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1)
258	ZEND_ARG_INFO(0, result)
259	ZEND_ARG_INFO(0, row)
260	ZEND_ARG_INFO(0, result_type)
261ZEND_END_ARG_INFO()
262
263ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1)
264	ZEND_ARG_INFO(0, result)
265	ZEND_ARG_INFO(0, row)
266ZEND_END_ARG_INFO()
267
268ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1)
269	ZEND_ARG_INFO(0, result)
270	ZEND_ARG_INFO(0, row)
271	ZEND_ARG_INFO(0, result_type)
272ZEND_END_ARG_INFO()
273
274ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1)
275	ZEND_ARG_INFO(0, result)
276	ZEND_ARG_INFO(0, row)
277	ZEND_ARG_INFO(0, class_name)
278	ZEND_ARG_INFO(0, l)
279	ZEND_ARG_INFO(0, ctor_params)
280ZEND_END_ARG_INFO()
281
282ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
283	ZEND_ARG_INFO(0, result)
284	ZEND_ARG_INFO(0, result_type)
285ZEND_END_ARG_INFO()
286
287ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
288	ZEND_ARG_INFO(0, result)
289	ZEND_ARG_INFO(0, column_number)
290ZEND_END_ARG_INFO()
291
292ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_seek, 0, 0, 2)
293	ZEND_ARG_INFO(0, result)
294	ZEND_ARG_INFO(0, offset)
295ZEND_END_ARG_INFO()
296
297ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_prtlen, 0, 0, 1)
298	ZEND_ARG_INFO(0, result)
299	ZEND_ARG_INFO(0, row)
300	ZEND_ARG_INFO(0, field_name_or_number)
301ZEND_END_ARG_INFO()
302
303ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_is_null, 0, 0, 1)
304	ZEND_ARG_INFO(0, result)
305	ZEND_ARG_INFO(0, row)
306	ZEND_ARG_INFO(0, field_name_or_number)
307ZEND_END_ARG_INFO()
308
309ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_free_result, 0, 0, 1)
310	ZEND_ARG_INFO(0, result)
311ZEND_END_ARG_INFO()
312
313ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_oid, 0, 0, 1)
314	ZEND_ARG_INFO(0, result)
315ZEND_END_ARG_INFO()
316
317ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_trace, 0, 0, 1)
318	ZEND_ARG_INFO(0, filename)
319	ZEND_ARG_INFO(0, mode)
320	ZEND_ARG_INFO(0, connection)
321ZEND_END_ARG_INFO()
322
323ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_untrace, 0, 0, 0)
324	ZEND_ARG_INFO(0, connection)
325ZEND_END_ARG_INFO()
326
327ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_create, 0, 0, 0)
328	ZEND_ARG_INFO(0, connection)
329	ZEND_ARG_INFO(0, large_object_id)
330ZEND_END_ARG_INFO()
331
332ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_unlink, 0, 0, 0)
333	ZEND_ARG_INFO(0, connection)
334	ZEND_ARG_INFO(0, large_object_oid)
335ZEND_END_ARG_INFO()
336
337ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_open, 0, 0, 0)
338	ZEND_ARG_INFO(0, connection)
339	ZEND_ARG_INFO(0, large_object_oid)
340	ZEND_ARG_INFO(0, mode)
341ZEND_END_ARG_INFO()
342
343ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_close, 0, 0, 1)
344	ZEND_ARG_INFO(0, large_object)
345ZEND_END_ARG_INFO()
346
347ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read, 0, 0, 1)
348	ZEND_ARG_INFO(0, large_object)
349	ZEND_ARG_INFO(0, len)
350ZEND_END_ARG_INFO()
351
352ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_write, 0, 0, 2)
353	ZEND_ARG_INFO(0, large_object)
354	ZEND_ARG_INFO(0, buf)
355	ZEND_ARG_INFO(0, len)
356ZEND_END_ARG_INFO()
357
358ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read_all, 0, 0, 1)
359	ZEND_ARG_INFO(0, large_object)
360ZEND_END_ARG_INFO()
361
362ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_import, 0, 0, 0)
363	ZEND_ARG_INFO(0, connection)
364	ZEND_ARG_INFO(0, filename)
365	ZEND_ARG_INFO(0, large_object_oid)
366ZEND_END_ARG_INFO()
367
368ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_export, 0, 0, 0)
369	ZEND_ARG_INFO(0, connection)
370	ZEND_ARG_INFO(0, objoid)
371	ZEND_ARG_INFO(0, filename)
372ZEND_END_ARG_INFO()
373
374ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_seek, 0, 0, 2)
375	ZEND_ARG_INFO(0, large_object)
376	ZEND_ARG_INFO(0, offset)
377	ZEND_ARG_INFO(0, whence)
378ZEND_END_ARG_INFO()
379
380ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_tell, 0, 0, 1)
381	ZEND_ARG_INFO(0, large_object)
382ZEND_END_ARG_INFO()
383
384#if HAVE_PG_LO_TRUNCATE
385ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_truncate, 0, 0, 1)
386	ZEND_ARG_INFO(0, large_object)
387	ZEND_ARG_INFO(0, size)
388ZEND_END_ARG_INFO()
389#endif
390
391#if HAVE_PQSETERRORVERBOSITY
392ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_error_verbosity, 0, 0, 0)
393	ZEND_ARG_INFO(0, connection)
394	ZEND_ARG_INFO(0, verbosity)
395ZEND_END_ARG_INFO()
396#endif
397
398#if HAVE_PQCLIENTENCODING
399ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_client_encoding, 0, 0, 0)
400	ZEND_ARG_INFO(0, connection)
401	ZEND_ARG_INFO(0, encoding)
402ZEND_END_ARG_INFO()
403
404ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_client_encoding, 0, 0, 0)
405	ZEND_ARG_INFO(0, connection)
406ZEND_END_ARG_INFO()
407#endif
408
409ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_end_copy, 0, 0, 0)
410	ZEND_ARG_INFO(0, connection)
411ZEND_END_ARG_INFO()
412
413ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_put_line, 0, 0, 0)
414	ZEND_ARG_INFO(0, connection)
415	ZEND_ARG_INFO(0, query)
416ZEND_END_ARG_INFO()
417
418ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_to, 0, 0, 2)
419	ZEND_ARG_INFO(0, connection)
420	ZEND_ARG_INFO(0, table_name)
421	ZEND_ARG_INFO(0, delimiter)
422	ZEND_ARG_INFO(0, null_as)
423ZEND_END_ARG_INFO()
424
425ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_from, 0, 0, 3)
426	ZEND_ARG_INFO(0, connection)
427	ZEND_ARG_INFO(0, table_name)
428	ZEND_ARG_INFO(0, rows)
429	ZEND_ARG_INFO(0, delimiter)
430	ZEND_ARG_INFO(0, null_as)
431ZEND_END_ARG_INFO()
432
433#if HAVE_PQESCAPE
434ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_string, 0, 0, 0)
435	ZEND_ARG_INFO(0, connection)
436	ZEND_ARG_INFO(0, data)
437ZEND_END_ARG_INFO()
438
439ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_bytea, 0, 0, 0)
440	ZEND_ARG_INFO(0, connection)
441	ZEND_ARG_INFO(0, data)
442ZEND_END_ARG_INFO()
443
444ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
445	ZEND_ARG_INFO(0, data)
446ZEND_END_ARG_INFO()
447#endif
448
449#if HAVE_PQESCAPE
450ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0)
451	ZEND_ARG_INFO(0, connection)
452	ZEND_ARG_INFO(0, data)
453ZEND_END_ARG_INFO()
454ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0)
455	ZEND_ARG_INFO(0, connection)
456	ZEND_ARG_INFO(0, data)
457ZEND_END_ARG_INFO()
458#endif
459
460ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1)
461	ZEND_ARG_INFO(0, result)
462ZEND_END_ARG_INFO()
463
464#if HAVE_PQRESULTERRORFIELD
465ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error_field, 0, 0, 2)
466	ZEND_ARG_INFO(0, result)
467	ZEND_ARG_INFO(0, fieldcode)
468ZEND_END_ARG_INFO()
469#endif
470
471ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_status, 0, 0, 1)
472	ZEND_ARG_INFO(0, connection)
473ZEND_END_ARG_INFO()
474
475#if HAVE_PGTRANSACTIONSTATUS
476ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_transaction_status, 0, 0, 1)
477	ZEND_ARG_INFO(0, connection)
478ZEND_END_ARG_INFO()
479#endif
480
481ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_reset, 0, 0, 1)
482	ZEND_ARG_INFO(0, connection)
483ZEND_END_ARG_INFO()
484
485ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_cancel_query, 0, 0, 1)
486	ZEND_ARG_INFO(0, connection)
487ZEND_END_ARG_INFO()
488
489ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_busy, 0, 0, 1)
490	ZEND_ARG_INFO(0, connection)
491ZEND_END_ARG_INFO()
492
493ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query, 0, 0, 2)
494	ZEND_ARG_INFO(0, connection)
495	ZEND_ARG_INFO(0, query)
496ZEND_END_ARG_INFO()
497
498#if HAVE_PQSENDQUERYPARAMS
499ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query_params, 0, 0, 3)
500	ZEND_ARG_INFO(0, connection)
501	ZEND_ARG_INFO(0, query)
502	ZEND_ARG_INFO(0, params)
503ZEND_END_ARG_INFO()
504#endif
505
506#if HAVE_PQSENDPREPARE
507ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 3)
508	ZEND_ARG_INFO(0, connection)
509	ZEND_ARG_INFO(0, stmtname)
510	ZEND_ARG_INFO(0, query)
511ZEND_END_ARG_INFO()
512#endif
513
514#if HAVE_PQSENDQUERYPREPARED
515ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 3)
516	ZEND_ARG_INFO(0, connection)
517	ZEND_ARG_INFO(0, stmtname)
518	ZEND_ARG_INFO(0, params)
519ZEND_END_ARG_INFO()
520#endif
521
522ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_result, 0, 0, 1)
523	ZEND_ARG_INFO(0, connection)
524ZEND_END_ARG_INFO()
525
526ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_status, 0, 0, 1)
527	ZEND_ARG_INFO(0, result)
528	ZEND_ARG_INFO(0, result_type)
529ZEND_END_ARG_INFO()
530
531ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_notify, 0, 0, 0)
532	ZEND_ARG_INFO(0, connection)
533	ZEND_ARG_INFO(0, e)
534ZEND_END_ARG_INFO()
535
536ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0)
537	ZEND_ARG_INFO(0, connection)
538ZEND_END_ARG_INFO()
539
540ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_socket, 0, 0, 1)
541	ZEND_ARG_INFO(0, connection)
542ZEND_END_ARG_INFO()
543
544ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_consume_input, 0, 0, 1)
545	ZEND_ARG_INFO(0, connection)
546ZEND_END_ARG_INFO()
547
548ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_flush, 0, 0, 1)
549	ZEND_ARG_INFO(0, connection)
550ZEND_END_ARG_INFO()
551
552ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2)
553	ZEND_ARG_INFO(0, db)
554	ZEND_ARG_INFO(0, table)
555ZEND_END_ARG_INFO()
556
557ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_convert, 0, 0, 3)
558	ZEND_ARG_INFO(0, db)
559	ZEND_ARG_INFO(0, table)
560	ZEND_ARG_INFO(0, values)
561	ZEND_ARG_INFO(0, options)
562ZEND_END_ARG_INFO()
563
564ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_insert, 0, 0, 3)
565	ZEND_ARG_INFO(0, db)
566	ZEND_ARG_INFO(0, table)
567	ZEND_ARG_INFO(0, values)
568	ZEND_ARG_INFO(0, options)
569ZEND_END_ARG_INFO()
570
571ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_update, 0, 0, 4)
572	ZEND_ARG_INFO(0, db)
573	ZEND_ARG_INFO(0, table)
574	ZEND_ARG_INFO(0, fields)
575	ZEND_ARG_INFO(0, ids)
576	ZEND_ARG_INFO(0, options)
577ZEND_END_ARG_INFO()
578
579ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_delete, 0, 0, 3)
580	ZEND_ARG_INFO(0, db)
581	ZEND_ARG_INFO(0, table)
582	ZEND_ARG_INFO(0, ids)
583	ZEND_ARG_INFO(0, options)
584ZEND_END_ARG_INFO()
585
586ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
587	ZEND_ARG_INFO(0, db)
588	ZEND_ARG_INFO(0, table)
589	ZEND_ARG_INFO(0, ids)
590	ZEND_ARG_INFO(0, options)
591	ZEND_ARG_INFO(0, result_type)
592ZEND_END_ARG_INFO()
593/* }}} */
594
595/* {{{ pgsql_functions[]
596 */
597const zend_function_entry pgsql_functions[] = {
598	/* connection functions */
599	PHP_FE(pg_connect,		arginfo_pg_connect)
600	PHP_FE(pg_pconnect,		arginfo_pg_pconnect)
601	PHP_FE(pg_connect_poll,	arginfo_pg_connect_poll)
602	PHP_FE(pg_close,		arginfo_pg_close)
603	PHP_FE(pg_connection_status,	arginfo_pg_connection_status)
604	PHP_FE(pg_connection_busy,		arginfo_pg_connection_busy)
605	PHP_FE(pg_connection_reset,		arginfo_pg_connection_reset)
606	PHP_FE(pg_host,			arginfo_pg_host)
607	PHP_FE(pg_dbname,		arginfo_pg_dbname)
608	PHP_FE(pg_port,			arginfo_pg_port)
609	PHP_FE(pg_tty,			arginfo_pg_tty)
610	PHP_FE(pg_options,		arginfo_pg_options)
611	PHP_FE(pg_version,		arginfo_pg_version)
612	PHP_FE(pg_ping,			arginfo_pg_ping)
613#if HAVE_PQPARAMETERSTATUS
614	PHP_FE(pg_parameter_status, arginfo_pg_parameter_status)
615#endif
616#if HAVE_PGTRANSACTIONSTATUS
617	PHP_FE(pg_transaction_status, arginfo_pg_transaction_status)
618#endif
619	/* query functions */
620	PHP_FE(pg_query,		arginfo_pg_query)
621#if HAVE_PQEXECPARAMS
622	PHP_FE(pg_query_params,		arginfo_pg_query_params)
623#endif
624#if HAVE_PQPREPARE
625	PHP_FE(pg_prepare,		arginfo_pg_prepare)
626#endif
627#if HAVE_PQEXECPREPARED
628	PHP_FE(pg_execute,		arginfo_pg_execute)
629#endif
630	PHP_FE(pg_send_query,	arginfo_pg_send_query)
631#if HAVE_PQSENDQUERYPARAMS
632	PHP_FE(pg_send_query_params,	arginfo_pg_send_query_params)
633#endif
634#if HAVE_PQSENDPREPARE
635	PHP_FE(pg_send_prepare,	arginfo_pg_send_prepare)
636#endif
637#if HAVE_PQSENDQUERYPREPARED
638	PHP_FE(pg_send_execute,	arginfo_pg_send_execute)
639#endif
640	PHP_FE(pg_cancel_query, arginfo_pg_cancel_query)
641	/* result functions */
642	PHP_FE(pg_fetch_result,	arginfo_pg_fetch_result)
643	PHP_FE(pg_fetch_row,	arginfo_pg_fetch_row)
644	PHP_FE(pg_fetch_assoc,	arginfo_pg_fetch_assoc)
645	PHP_FE(pg_fetch_array,	arginfo_pg_fetch_array)
646	PHP_FE(pg_fetch_object,	arginfo_pg_fetch_object)
647	PHP_FE(pg_fetch_all,	arginfo_pg_fetch_all)
648	PHP_FE(pg_fetch_all_columns,	arginfo_pg_fetch_all_columns)
649#if HAVE_PQCMDTUPLES
650	PHP_FE(pg_affected_rows,arginfo_pg_affected_rows)
651#endif
652	PHP_FE(pg_get_result,	arginfo_pg_get_result)
653	PHP_FE(pg_result_seek,	arginfo_pg_result_seek)
654	PHP_FE(pg_result_status,arginfo_pg_result_status)
655	PHP_FE(pg_free_result,	arginfo_pg_free_result)
656	PHP_FE(pg_last_oid,	    arginfo_pg_last_oid)
657	PHP_FE(pg_num_rows,		arginfo_pg_num_rows)
658	PHP_FE(pg_num_fields,	arginfo_pg_num_fields)
659	PHP_FE(pg_field_name,	arginfo_pg_field_name)
660	PHP_FE(pg_field_num,	arginfo_pg_field_num)
661	PHP_FE(pg_field_size,	arginfo_pg_field_size)
662	PHP_FE(pg_field_type,	arginfo_pg_field_type)
663	PHP_FE(pg_field_type_oid, arginfo_pg_field_type_oid)
664	PHP_FE(pg_field_prtlen,	arginfo_pg_field_prtlen)
665	PHP_FE(pg_field_is_null,arginfo_pg_field_is_null)
666#ifdef HAVE_PQFTABLE
667	PHP_FE(pg_field_table,  arginfo_pg_field_table)
668#endif
669	/* async message function */
670	PHP_FE(pg_get_notify,   arginfo_pg_get_notify)
671	PHP_FE(pg_socket,		arginfo_pg_socket)
672	PHP_FE(pg_consume_input,arginfo_pg_consume_input)
673	PHP_FE(pg_flush,		arginfo_pg_flush)
674	PHP_FE(pg_get_pid,      arginfo_pg_get_pid)
675	/* error message functions */
676	PHP_FE(pg_result_error, arginfo_pg_result_error)
677#if HAVE_PQRESULTERRORFIELD
678	PHP_FE(pg_result_error_field, arginfo_pg_result_error_field)
679#endif
680	PHP_FE(pg_last_error,   arginfo_pg_last_error)
681	PHP_FE(pg_last_notice,  arginfo_pg_last_notice)
682	/* copy functions */
683	PHP_FE(pg_put_line,		arginfo_pg_put_line)
684	PHP_FE(pg_end_copy,		arginfo_pg_end_copy)
685	PHP_FE(pg_copy_to,      arginfo_pg_copy_to)
686	PHP_FE(pg_copy_from,    arginfo_pg_copy_from)
687	/* debug functions */
688	PHP_FE(pg_trace,		arginfo_pg_trace)
689	PHP_FE(pg_untrace,		arginfo_pg_untrace)
690	/* large object functions */
691	PHP_FE(pg_lo_create,	arginfo_pg_lo_create)
692	PHP_FE(pg_lo_unlink,	arginfo_pg_lo_unlink)
693	PHP_FE(pg_lo_open,		arginfo_pg_lo_open)
694	PHP_FE(pg_lo_close,		arginfo_pg_lo_close)
695	PHP_FE(pg_lo_read,		arginfo_pg_lo_read)
696	PHP_FE(pg_lo_write,		arginfo_pg_lo_write)
697	PHP_FE(pg_lo_read_all,	arginfo_pg_lo_read_all)
698	PHP_FE(pg_lo_import,	arginfo_pg_lo_import)
699	PHP_FE(pg_lo_export,	arginfo_pg_lo_export)
700	PHP_FE(pg_lo_seek,		arginfo_pg_lo_seek)
701	PHP_FE(pg_lo_tell,		arginfo_pg_lo_tell)
702#if HAVE_PG_LO_TRUNCATE
703	PHP_FE(pg_lo_truncate,	arginfo_pg_lo_truncate)
704#endif
705	/* utility functions */
706#if HAVE_PQESCAPE
707	PHP_FE(pg_escape_string,	arginfo_pg_escape_string)
708	PHP_FE(pg_escape_bytea, 	arginfo_pg_escape_bytea)
709	PHP_FE(pg_unescape_bytea, 	arginfo_pg_unescape_bytea)
710	PHP_FE(pg_escape_literal,	arginfo_pg_escape_literal)
711	PHP_FE(pg_escape_identifier,	arginfo_pg_escape_identifier)
712#endif
713#if HAVE_PQSETERRORVERBOSITY
714	PHP_FE(pg_set_error_verbosity,	arginfo_pg_set_error_verbosity)
715#endif
716#if HAVE_PQCLIENTENCODING
717	PHP_FE(pg_client_encoding,		arginfo_pg_client_encoding)
718	PHP_FE(pg_set_client_encoding,	arginfo_pg_set_client_encoding)
719#endif
720	/* misc function */
721	PHP_FE(pg_meta_data,	arginfo_pg_meta_data)
722	PHP_FE(pg_convert,      arginfo_pg_convert)
723	PHP_FE(pg_insert,       arginfo_pg_insert)
724	PHP_FE(pg_update,       arginfo_pg_update)
725	PHP_FE(pg_delete,       arginfo_pg_delete)
726	PHP_FE(pg_select,       arginfo_pg_select)
727	/* aliases for downwards compatibility */
728	PHP_FALIAS(pg_exec,          pg_query,          arginfo_pg_query)
729	PHP_FALIAS(pg_getlastoid,    pg_last_oid,       arginfo_pg_last_oid)
730#if HAVE_PQCMDTUPLES
731	PHP_FALIAS(pg_cmdtuples,	 pg_affected_rows,  arginfo_pg_affected_rows)
732#endif
733	PHP_FALIAS(pg_errormessage,	 pg_last_error,     arginfo_pg_last_error)
734	PHP_FALIAS(pg_numrows,		 pg_num_rows,       arginfo_pg_num_rows)
735	PHP_FALIAS(pg_numfields,	 pg_num_fields,     arginfo_pg_num_fields)
736	PHP_FALIAS(pg_fieldname,	 pg_field_name,     arginfo_pg_field_name)
737	PHP_FALIAS(pg_fieldsize,     pg_field_size,     arginfo_pg_field_size)
738	PHP_FALIAS(pg_fieldtype,	 pg_field_type,     arginfo_pg_field_type)
739	PHP_FALIAS(pg_fieldnum,	     pg_field_num,      arginfo_pg_field_num)
740	PHP_FALIAS(pg_fieldprtlen,	 pg_field_prtlen,   arginfo_pg_field_prtlen)
741	PHP_FALIAS(pg_fieldisnull,	 pg_field_is_null,  arginfo_pg_field_is_null)
742	PHP_FALIAS(pg_freeresult,    pg_free_result,    arginfo_pg_free_result)
743	PHP_FALIAS(pg_result,	     pg_fetch_result,   arginfo_pg_get_result)
744	PHP_FALIAS(pg_loreadall,	 pg_lo_read_all,    arginfo_pg_lo_read_all)
745	PHP_FALIAS(pg_locreate,	     pg_lo_create,      arginfo_pg_lo_create)
746	PHP_FALIAS(pg_lounlink,	     pg_lo_unlink,      arginfo_pg_lo_unlink)
747	PHP_FALIAS(pg_loopen,	     pg_lo_open,        arginfo_pg_lo_open)
748	PHP_FALIAS(pg_loclose,	     pg_lo_close,       arginfo_pg_lo_close)
749	PHP_FALIAS(pg_loread,	     pg_lo_read,        arginfo_pg_lo_read)
750	PHP_FALIAS(pg_lowrite,	     pg_lo_write,       arginfo_pg_lo_write)
751	PHP_FALIAS(pg_loimport,	     pg_lo_import,      arginfo_pg_lo_import)
752	PHP_FALIAS(pg_loexport,	     pg_lo_export,      arginfo_pg_lo_export)
753#if HAVE_PQCLIENTENCODING
754	PHP_FALIAS(pg_clientencoding,		pg_client_encoding,		arginfo_pg_client_encoding)
755	PHP_FALIAS(pg_setclientencoding,	pg_set_client_encoding,	arginfo_pg_set_client_encoding)
756#endif
757	PHP_FE_END
758};
759/* }}} */
760
761/* {{{ pgsql_module_entry
762 */
763zend_module_entry pgsql_module_entry = {
764	STANDARD_MODULE_HEADER,
765	"pgsql",
766	pgsql_functions,
767	PHP_MINIT(pgsql),
768	PHP_MSHUTDOWN(pgsql),
769	PHP_RINIT(pgsql),
770	PHP_RSHUTDOWN(pgsql),
771	PHP_MINFO(pgsql),
772	PHP_PGSQL_VERSION,
773	PHP_MODULE_GLOBALS(pgsql),
774	PHP_GINIT(pgsql),
775	NULL,
776	NULL,
777	STANDARD_MODULE_PROPERTIES_EX
778};
779/* }}} */
780
781#ifdef COMPILE_DL_PGSQL
782#ifdef ZTS
783ZEND_TSRMLS_CACHE_DEFINE()
784#endif
785ZEND_GET_MODULE(pgsql)
786#endif
787
788static int le_link, le_plink, le_result, le_lofp, le_string;
789
790/* Compatibility definitions */
791
792#ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
793#define pg_encoding_to_char(x) "SQL_ASCII"
794#endif
795
796#if !HAVE_PQESCAPE_CONN
797#define PQescapeStringConn(conn, to, from, len, error) PQescapeString(to, from, len)
798#endif
799
800#if HAVE_PQESCAPELITERAL
801#define PGSQLescapeLiteral(conn, str, len) PQescapeLiteral(conn, str, len)
802#define PGSQLescapeIdentifier(conn, str, len) PQescapeIdentifier(conn, str, len)
803#define PGSQLfree(a) PQfreemem(a)
804#else
805#define PGSQLescapeLiteral(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 0)
806#define PGSQLescapeLiteral2(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 1)
807#define PGSQLescapeIdentifier(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 0, 0)
808#define PGSQLfree(a) efree(a)
809
810/* emulate libpq's PQescapeInternal() 9.0 or later */
811static char *php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe) /* {{{ */
812{
813	char *result, *rp, *s;
814	size_t tmp_len;
815
816	if (!conn) {
817		return NULL;
818	}
819
820	/* allocate enough memory */
821	rp = result = (char *)safe_emalloc(len, 2, 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */
822
823	if (escape_literal) {
824		size_t new_len;
825
826		if (safe) {
827			char *tmp = (char *)safe_emalloc(len, 2, 1);
828			*rp++ = '\'';
829			/* PQescapeString does not escape \, but it handles multibyte chars safely.
830			   This escape is incompatible with PQescapeLiteral. */
831			new_len = PQescapeStringConn(conn, tmp, str, len, NULL);
832			strncpy(rp, tmp, new_len);
833			efree(tmp);
834			rp += new_len;
835		} else {
836			char *encoding;
837			/* This is compatible with PQescapeLiteral, but it cannot handle multbyte chars
838			   such as SJIS, BIG5. Raise warning and return NULL by checking
839			   client_encoding. */
840			encoding = (char *) pg_encoding_to_char(PQclientEncoding(conn));
841			if (!strncmp(encoding, "SJIS", sizeof("SJIS")-1) ||
842				!strncmp(encoding, "SHIFT_JIS_2004", sizeof("SHIFT_JIS_2004")-1) ||
843				!strncmp(encoding, "BIG5", sizeof("BIG5")-1) ||
844				!strncmp(encoding, "GB18030", sizeof("GB18030")-1) ||
845				!strncmp(encoding, "GBK", sizeof("GBK")-1) ||
846				!strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) ||
847				!strncmp(encoding, "UHC", sizeof("UHC")-1) ) {
848
849				php_error_docref(NULL, E_WARNING, "Unsafe encoding is used. Do not use '%s' encoding or use PostgreSQL 9.0 or later libpq.", encoding);
850			}
851			/* check backslashes */
852			tmp_len = strspn(str, "\\");
853			if (tmp_len != len) {
854				/* add " E" for escaping slashes */
855				*rp++ = ' ';
856				*rp++ = 'E';
857			}
858			*rp++ = '\'';
859			for (s = (char *)str; s - str < len; ++s) {
860				if (*s == '\'' || *s == '\\') {
861					*rp++ = *s;
862					*rp++ = *s;
863				} else {
864					*rp++ = *s;
865				}
866			}
867		}
868		*rp++ = '\'';
869	} else {
870		/* Identifier escape. */
871		*rp++ = '"';
872		for (s = (char *)str; s - str < len; ++s) {
873			if (*s == '"') {
874				*rp++ = '"';
875				*rp++ = '"';
876			} else {
877				*rp++ = *s;
878			}
879		}
880		*rp++ = '"';
881	}
882	*rp = '\0';
883
884	return result;
885}
886/* }}} */
887#endif
888
889/* {{{ _php_pgsql_trim_message */
890static char * _php_pgsql_trim_message(const char *message, size_t *len)
891{
892	register size_t i = strlen(message);
893
894	if (i>2 && (message[i-2] == '\r' || message[i-2] == '\n') && message[i-1] == '.') {
895		--i;
896	}
897	while (i>1 && (message[i-1] == '\r' || message[i-1] == '\n')) {
898		--i;
899	}
900	if (len) {
901		*len = i;
902	}
903	return estrndup(message, i);
904}
905/* }}} */
906
907/* {{{ _php_pgsql_trim_result */
908static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
909{
910	return *buf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL);
911}
912/* }}} */
913
914#define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
915
916#define PHP_PQ_ERROR(text, pgsql) {										\
917		char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
918		php_error_docref(NULL, E_WARNING, text, msgbuf);		\
919		efree(msgbuf);													\
920} \
921
922/* {{{ php_pgsql_set_default_link
923 */
924static void php_pgsql_set_default_link(zend_resource *res)
925{
926	GC_REFCOUNT(res)++;
927
928	if (PGG(default_link) != NULL) {
929		zend_list_delete(PGG(default_link));
930	}
931
932	PGG(default_link) = res;
933}
934/* }}} */
935
936/* {{{ _close_pgsql_link
937 */
938static void _close_pgsql_link(zend_resource *rsrc)
939{
940	PGconn *link = (PGconn *)rsrc->ptr;
941	PGresult *res;
942
943	while ((res = PQgetResult(link))) {
944		PQclear(res);
945	}
946	PQfinish(link);
947	PGG(num_links)--;
948}
949/* }}} */
950
951/* {{{ _close_pgsql_plink
952 */
953static void _close_pgsql_plink(zend_resource *rsrc)
954{
955	PGconn *link = (PGconn *)rsrc->ptr;
956	PGresult *res;
957
958	while ((res = PQgetResult(link))) {
959		PQclear(res);
960	}
961	PQfinish(link);
962	PGG(num_persistent)--;
963	PGG(num_links)--;
964}
965/* }}} */
966
967/* {{{ _php_pgsql_notice_handler
968 */
969static void _php_pgsql_notice_handler(void *resource_id, const char *message)
970{
971	zval *notices;
972	zval tmp;
973	char *trimed_message;
974	size_t trimed_message_len;
975
976	if (! PGG(ignore_notices)) {
977		notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
978		if (!notices) {
979			array_init(&tmp);
980			notices = &tmp;
981			zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
982		}
983		trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
984		if (PGG(log_notices)) {
985			php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
986		}
987		add_next_index_stringl(notices, trimed_message, trimed_message_len);
988		efree(trimed_message);
989	}
990}
991/* }}} */
992
993/* {{{ _rollback_transactions
994 */
995static int _rollback_transactions(zval *el)
996{
997	PGconn *link;
998	PGresult *res;
999	int orig;
1000	zend_resource *rsrc = Z_RES_P(el);
1001
1002	if (rsrc->type != le_plink)
1003		return 0;
1004
1005	link = (PGconn *) rsrc->ptr;
1006
1007	if (PQ_SETNONBLOCKING(link, 0)) {
1008		php_error_docref("ref.pgsql", E_NOTICE, "Cannot set connection to blocking mode");
1009		return -1;
1010	}
1011
1012	while ((res = PQgetResult(link))) {
1013		PQclear(res);
1014	}
1015#if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1016	if ((PQprotocolVersion(link) >= 3 && PQtransactionStatus(link) != PQTRANS_IDLE) || PQprotocolVersion(link) < 3)
1017#endif
1018	{
1019		orig = PGG(ignore_notices);
1020		PGG(ignore_notices) = 1;
1021#if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1022		res = PQexec(link,"ROLLBACK;");
1023#else
1024		res = PQexec(link,"BEGIN;");
1025		PQclear(res);
1026		res = PQexec(link,"ROLLBACK;");
1027#endif
1028		PQclear(res);
1029		PGG(ignore_notices) = orig;
1030	}
1031
1032	return 0;
1033}
1034/* }}} */
1035
1036/* {{{ _free_ptr
1037 */
1038static void _free_ptr(zend_resource *rsrc)
1039{
1040	pgLofp *lofp = (pgLofp *)rsrc->ptr;
1041	efree(lofp);
1042}
1043/* }}} */
1044
1045/* {{{ _free_result
1046 */
1047static void _free_result(zend_resource *rsrc)
1048{
1049	pgsql_result_handle *pg_result = (pgsql_result_handle *)rsrc->ptr;
1050
1051	PQclear(pg_result->result);
1052	efree(pg_result);
1053}
1054/* }}} */
1055
1056static int _php_pgsql_detect_identifier_escape(const char *identifier, size_t len) /* {{{ */
1057{
1058	size_t i;
1059
1060	/* Handle edge case. Cannot be a escaped string */
1061	if (len <= 2) {
1062		return FAILURE;
1063	}
1064	/* Detect double qoutes */
1065	if (identifier[0] == '"' && identifier[len-1] == '"') {
1066		/* Detect wrong format of " inside of escaped string */
1067		for (i = 1; i < len-1; i++) {
1068			if (identifier[i] == '"' && (identifier[++i] != '"' || i == len-1)) {
1069				return FAILURE;
1070			}
1071		}
1072	} else {
1073		return FAILURE;
1074	}
1075	/* Escaped properly */
1076	return SUCCESS;
1077}
1078/* }}} */
1079
1080/* {{{ PHP_INI
1081 */
1082PHP_INI_BEGIN()
1083STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent",      "1",  PHP_INI_SYSTEM, OnUpdateBool, allow_persistent,      zend_pgsql_globals, pgsql_globals)
1084STD_PHP_INI_ENTRY_EX("pgsql.max_persistent",       "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_persistent,        zend_pgsql_globals, pgsql_globals, display_link_numbers)
1085STD_PHP_INI_ENTRY_EX("pgsql.max_links",            "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_links,             zend_pgsql_globals, pgsql_globals, display_link_numbers)
1086STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0",  PHP_INI_SYSTEM, OnUpdateBool, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
1087STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice",         "0",  PHP_INI_ALL,    OnUpdateBool, ignore_notices,        zend_pgsql_globals, pgsql_globals)
1088STD_PHP_INI_BOOLEAN( "pgsql.log_notice",            "0",  PHP_INI_ALL,    OnUpdateBool, log_notices,           zend_pgsql_globals, pgsql_globals)
1089PHP_INI_END()
1090/* }}} */
1091
1092/* {{{ PHP_GINIT_FUNCTION
1093 */
1094static PHP_GINIT_FUNCTION(pgsql)
1095{
1096#if defined(COMPILE_DL_PGSQL) && defined(ZTS)
1097	ZEND_TSRMLS_CACHE_UPDATE();
1098#endif
1099	memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
1100	/* Initilize notice message hash at MINIT only */
1101	zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1102}
1103/* }}} */
1104
1105/* {{{ PHP_MINIT_FUNCTION
1106 */
1107PHP_MINIT_FUNCTION(pgsql)
1108{
1109	REGISTER_INI_ENTRIES();
1110
1111	le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
1112	le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
1113	le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number);
1114	le_lofp = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql large object", module_number);
1115	le_string = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql string", module_number);
1116#if HAVE_PG_CONFIG_H
1117	/* PG_VERSION - libpq version */
1118	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION", PG_VERSION, CONST_CS | CONST_PERSISTENT);
1119	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION_STR", PG_VERSION_STR, CONST_CS | CONST_PERSISTENT);
1120#endif
1121	/* For connection option */
1122	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT);
1123	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_ASYNC", PGSQL_CONNECT_ASYNC, CONST_CS | CONST_PERSISTENT);
1124	/* For pg_fetch_array() */
1125	REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
1126	REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
1127	REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
1128	/* For pg_last_notice() */
1129	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
1130	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
1131	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
1132	/* For pg_connection_status() */
1133	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
1134	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
1135	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_STARTED", CONNECTION_STARTED, CONST_CS | CONST_PERSISTENT);
1136	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_MADE", CONNECTION_MADE, CONST_CS | CONST_PERSISTENT);
1137	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AWAITING_RESPONSE", CONNECTION_AWAITING_RESPONSE, CONST_CS | CONST_PERSISTENT);
1138	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AUTH_OK", CONNECTION_AUTH_OK, CONST_CS | CONST_PERSISTENT);
1139#ifdef CONNECTION_SSL_STARTUP
1140	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SSL_STARTUP", CONNECTION_SSL_STARTUP, CONST_CS | CONST_PERSISTENT);
1141#endif
1142	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SETENV", CONNECTION_SETENV, CONST_CS | CONST_PERSISTENT);
1143	/* For pg_connect_poll() */
1144	REGISTER_LONG_CONSTANT("PGSQL_POLLING_FAILED", PGRES_POLLING_FAILED, CONST_CS | CONST_PERSISTENT);
1145	REGISTER_LONG_CONSTANT("PGSQL_POLLING_READING", PGRES_POLLING_READING, CONST_CS | CONST_PERSISTENT);
1146	REGISTER_LONG_CONSTANT("PGSQL_POLLING_WRITING", PGRES_POLLING_WRITING, CONST_CS | CONST_PERSISTENT);
1147	REGISTER_LONG_CONSTANT("PGSQL_POLLING_OK", PGRES_POLLING_OK, CONST_CS | CONST_PERSISTENT);
1148	REGISTER_LONG_CONSTANT("PGSQL_POLLING_ACTIVE", PGRES_POLLING_ACTIVE, CONST_CS | CONST_PERSISTENT);
1149#if HAVE_PGTRANSACTIONSTATUS
1150	/* For pg_transaction_status() */
1151	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT);
1152	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_ACTIVE", PQTRANS_ACTIVE, CONST_CS | CONST_PERSISTENT);
1153	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INTRANS", PQTRANS_INTRANS, CONST_CS | CONST_PERSISTENT);
1154	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INERROR", PQTRANS_INERROR, CONST_CS | CONST_PERSISTENT);
1155	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_UNKNOWN", PQTRANS_UNKNOWN, CONST_CS | CONST_PERSISTENT);
1156#endif
1157#if HAVE_PQSETERRORVERBOSITY
1158	/* For pg_set_error_verbosity() */
1159	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_TERSE", PQERRORS_TERSE, CONST_CS | CONST_PERSISTENT);
1160	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_DEFAULT", PQERRORS_DEFAULT, CONST_CS | CONST_PERSISTENT);
1161	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_VERBOSE", PQERRORS_VERBOSE, CONST_CS | CONST_PERSISTENT);
1162#endif
1163	/* For lo_seek() */
1164	REGISTER_LONG_CONSTANT("PGSQL_SEEK_SET", SEEK_SET, CONST_CS | CONST_PERSISTENT);
1165	REGISTER_LONG_CONSTANT("PGSQL_SEEK_CUR", SEEK_CUR, CONST_CS | CONST_PERSISTENT);
1166	REGISTER_LONG_CONSTANT("PGSQL_SEEK_END", SEEK_END, CONST_CS | CONST_PERSISTENT);
1167	/* For pg_result_status() return value type */
1168	REGISTER_LONG_CONSTANT("PGSQL_STATUS_LONG", PGSQL_STATUS_LONG, CONST_CS | CONST_PERSISTENT);
1169	REGISTER_LONG_CONSTANT("PGSQL_STATUS_STRING", PGSQL_STATUS_STRING, CONST_CS | CONST_PERSISTENT);
1170	/* For pg_result_status() return value */
1171	REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_CS | CONST_PERSISTENT);
1172	REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_CS | CONST_PERSISTENT);
1173	REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_CS | CONST_PERSISTENT);
1174	REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_CS | CONST_PERSISTENT);
1175	REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_CS | CONST_PERSISTENT);
1176	REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_CS | CONST_PERSISTENT);
1177	REGISTER_LONG_CONSTANT("PGSQL_NONFATAL_ERROR", PGRES_NONFATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1178	REGISTER_LONG_CONSTANT("PGSQL_FATAL_ERROR", PGRES_FATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1179#if HAVE_PQRESULTERRORFIELD
1180	/* For pg_result_error_field() field codes */
1181	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY", PG_DIAG_SEVERITY, CONST_CS | CONST_PERSISTENT);
1182	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SQLSTATE", PG_DIAG_SQLSTATE, CONST_CS | CONST_PERSISTENT);
1183	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_PRIMARY", PG_DIAG_MESSAGE_PRIMARY, CONST_CS | CONST_PERSISTENT);
1184	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_DETAIL", PG_DIAG_MESSAGE_DETAIL, CONST_CS | CONST_PERSISTENT);
1185	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_HINT", PG_DIAG_MESSAGE_HINT, CONST_CS | CONST_PERSISTENT);
1186	REGISTER_LONG_CONSTANT("PGSQL_DIAG_STATEMENT_POSITION", PG_DIAG_STATEMENT_POSITION, CONST_CS | CONST_PERSISTENT);
1187#ifdef PG_DIAG_INTERNAL_POSITION
1188	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_POSITION", PG_DIAG_INTERNAL_POSITION, CONST_CS | CONST_PERSISTENT);
1189#endif
1190#ifdef PG_DIAG_INTERNAL_QUERY
1191	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_QUERY", PG_DIAG_INTERNAL_QUERY, CONST_CS | CONST_PERSISTENT);
1192#endif
1193	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONTEXT", PG_DIAG_CONTEXT, CONST_CS | CONST_PERSISTENT);
1194	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FILE", PG_DIAG_SOURCE_FILE, CONST_CS | CONST_PERSISTENT);
1195	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_LINE", PG_DIAG_SOURCE_LINE, CONST_CS | CONST_PERSISTENT);
1196	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FUNCTION", PG_DIAG_SOURCE_FUNCTION, CONST_CS | CONST_PERSISTENT);
1197#endif
1198	/* pg_convert options */
1199	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_DEFAULT", PGSQL_CONV_IGNORE_DEFAULT, CONST_CS | CONST_PERSISTENT);
1200	REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT);
1201	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT);
1202	/* pg_insert/update/delete/select options */
1203	REGISTER_LONG_CONSTANT("PGSQL_DML_ESCAPE", PGSQL_DML_ESCAPE, CONST_CS | CONST_PERSISTENT);
1204	REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT);
1205	REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT);
1206	REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT);
1207	REGISTER_LONG_CONSTANT("PGSQL_DML_STRING", PGSQL_DML_STRING, CONST_CS | CONST_PERSISTENT);
1208	return SUCCESS;
1209}
1210/* }}} */
1211
1212/* {{{ PHP_MSHUTDOWN_FUNCTION
1213 */
1214PHP_MSHUTDOWN_FUNCTION(pgsql)
1215{
1216	UNREGISTER_INI_ENTRIES();
1217	zend_hash_destroy(&PGG(notices));
1218
1219	return SUCCESS;
1220}
1221/* }}} */
1222
1223/* {{{ PHP_RINIT_FUNCTION
1224 */
1225PHP_RINIT_FUNCTION(pgsql)
1226{
1227	PGG(default_link) = NULL;
1228	PGG(num_links) = PGG(num_persistent);
1229	return SUCCESS;
1230}
1231/* }}} */
1232
1233/* {{{ PHP_RSHUTDOWN_FUNCTION
1234 */
1235PHP_RSHUTDOWN_FUNCTION(pgsql)
1236{
1237	/* clean up notice messages */
1238	zend_hash_clean(&PGG(notices));
1239	/* clean up persistent connection */
1240	zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions);
1241	return SUCCESS;
1242}
1243/* }}} */
1244
1245/* {{{ PHP_MINFO_FUNCTION
1246 */
1247PHP_MINFO_FUNCTION(pgsql)
1248{
1249	char buf[256];
1250
1251	php_info_print_table_start();
1252	php_info_print_table_header(2, "PostgreSQL Support", "enabled");
1253#if HAVE_PG_CONFIG_H
1254	php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION);
1255	php_info_print_table_row(2, "PostgreSQL(libpq) ", PG_VERSION_STR);
1256#ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
1257	php_info_print_table_row(2, "Multibyte character support", "enabled");
1258#else
1259	php_info_print_table_row(2, "Multibyte character support", "disabled");
1260#endif
1261#if defined(USE_SSL) || defined(USE_OPENSSL)
1262	php_info_print_table_row(2, "SSL support", "enabled");
1263#else
1264	php_info_print_table_row(2, "SSL support", "disabled");
1265#endif
1266#endif /* HAVE_PG_CONFIG_H */
1267	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_persistent));
1268	php_info_print_table_row(2, "Active Persistent Links", buf);
1269	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_links));
1270	php_info_print_table_row(2, "Active Links", buf);
1271	php_info_print_table_end();
1272
1273	DISPLAY_INI_ENTRIES();
1274}
1275/* }}} */
1276
1277/* {{{ php_pgsql_do_connect
1278 */
1279static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
1280{
1281	char *host=NULL,*port=NULL,*options=NULL,*tty=NULL,*dbname=NULL,*connstring=NULL;
1282	PGconn *pgsql;
1283	smart_str str = {0};
1284	zval *args;
1285	uint32_t i;
1286	int connect_type = 0;
1287	PGresult *pg_result;
1288
1289	args = (zval *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1290	if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5
1291			|| zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
1292		efree(args);
1293		WRONG_PARAM_COUNT;
1294	}
1295
1296	smart_str_appends(&str, "pgsql");
1297
1298	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1299		/* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent connections
1300		 * can re-use this connection. Bug #39979
1301		 */
1302		if (i == 1 && ZEND_NUM_ARGS() == 2 && Z_TYPE(args[i]) == IS_LONG) {
1303			if (Z_LVAL(args[1]) == PGSQL_CONNECT_FORCE_NEW) {
1304				continue;
1305			} else if (Z_LVAL(args[1]) & PGSQL_CONNECT_FORCE_NEW) {
1306				smart_str_append_long(&str, Z_LVAL(args[1]) ^ PGSQL_CONNECT_FORCE_NEW);
1307			}
1308		}
1309		ZVAL_STR(&args[i], zval_get_string(&args[i]));
1310		smart_str_appendc(&str, '_');
1311		smart_str_appendl(&str, Z_STRVAL(args[i]), Z_STRLEN(args[i]));
1312	}
1313
1314	smart_str_0(&str);
1315
1316	if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */
1317		connstring = Z_STRVAL(args[0]);
1318	} else if (ZEND_NUM_ARGS() == 2 ) { /* Safe to add conntype_option, since 2 args was illegal */
1319		connstring = Z_STRVAL(args[0]);
1320		convert_to_long_ex(&args[1]);
1321		connect_type = (int)Z_LVAL(args[1]);
1322	} else {
1323		host = Z_STRVAL(args[0]);
1324		port = Z_STRVAL(args[1]);
1325		dbname = Z_STRVAL(args[ZEND_NUM_ARGS()-1]);
1326
1327		switch (ZEND_NUM_ARGS()) {
1328		case 5:
1329			tty = Z_STRVAL(args[3]);
1330			/* fall through */
1331		case 4:
1332			options = Z_STRVAL(args[2]);
1333			break;
1334		}
1335	}
1336
1337	if (persistent && PGG(allow_persistent)) {
1338		zend_resource *le;
1339
1340		/* try to find if we already have this link in our persistent list */
1341		if ((le = zend_hash_find_ptr(&EG(persistent_list), str.s)) == NULL) {  /* we don't */
1342			zend_resource new_le;
1343
1344			if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1345				php_error_docref(NULL, E_WARNING,
1346								 "Cannot create new link. Too many open links (%pd)", PGG(num_links));
1347				goto err;
1348			}
1349			if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
1350				php_error_docref(NULL, E_WARNING,
1351								 "Cannot create new link. Too many open persistent links (%pd)", PGG(num_persistent));
1352				goto err;
1353			}
1354
1355			/* create the link */
1356			if (connstring) {
1357				pgsql = PQconnectdb(connstring);
1358			} else {
1359				pgsql = PQsetdb(host, port, options, tty, dbname);
1360			}
1361			if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) {
1362				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
1363				if (pgsql) {
1364					PQfinish(pgsql);
1365				}
1366				goto err;
1367			}
1368
1369			/* hash it up */
1370			new_le.type = le_plink;
1371			new_le.ptr = pgsql;
1372			if (zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(str.s), ZSTR_LEN(str.s), &new_le, sizeof(zend_resource)) == NULL) {
1373				goto err;
1374			}
1375			PGG(num_links)++;
1376			PGG(num_persistent)++;
1377		} else {  /* we do */
1378			if (le->type != le_plink) {
1379				goto err;
1380			}
1381			/* ensure that the link did not die */
1382			if (PGG(auto_reset_persistent) & 1) {
1383				/* need to send & get something from backend to
1384				   make sure we catch CONNECTION_BAD every time */
1385				PGresult *pg_result;
1386				pg_result = PQexec(le->ptr, "select 1");
1387				PQclear(pg_result);
1388			}
1389			if (PQstatus(le->ptr) == CONNECTION_BAD) { /* the link died */
1390				if (le->ptr == NULL) {
1391					if (connstring) {
1392						le->ptr = PQconnectdb(connstring);
1393					} else {
1394						le->ptr = PQsetdb(host,port,options,tty,dbname);
1395					}
1396				}
1397				else {
1398					PQreset(le->ptr);
1399				}
1400				if (le->ptr == NULL || PQstatus(le->ptr) == CONNECTION_BAD) {
1401					php_error_docref(NULL, E_WARNING,"PostgreSQL link lost, unable to reconnect");
1402					zend_hash_del(&EG(persistent_list), str.s);
1403					goto err;
1404				}
1405			}
1406			pgsql = (PGconn *) le->ptr;
1407#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
1408			if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 7.2) {
1409#else
1410			if (atof(PG_VERSION) >= 7.2) {
1411#endif
1412				pg_result = PQexec(pgsql, "RESET ALL;");
1413				PQclear(pg_result);
1414			}
1415		}
1416		RETVAL_RES(zend_register_resource(pgsql, le_plink));
1417	} else { /* Non persistent connection */
1418		zend_resource *index_ptr, new_index_ptr;
1419
1420		/* first we check the hash for the hashed_details key.  if it exists,
1421		 * it should point us to the right offset where the actual pgsql link sits.
1422		 * if it doesn't, open a new pgsql link, add it to the resource list,
1423		 * and add a pointer to it with hashed_details as the key.
1424		 */
1425		if (!(connect_type & PGSQL_CONNECT_FORCE_NEW)
1426			&& (index_ptr = zend_hash_find_ptr(&EG(regular_list), str.s)) != NULL) {
1427			zend_resource *link;
1428
1429			if (index_ptr->type != le_index_ptr) {
1430				goto err;
1431			}
1432
1433			link = (zend_resource *)index_ptr->ptr;
1434			if (link->ptr && (link->type == le_link || link->type == le_plink)) {
1435				php_pgsql_set_default_link(link);
1436				GC_REFCOUNT(link)++;
1437				RETVAL_RES(link);
1438				goto cleanup;
1439			} else {
1440				zend_hash_del(&EG(regular_list), str.s);
1441			}
1442		}
1443		if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1444			php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (%pd)", PGG(num_links));
1445			goto err;
1446		}
1447
1448		/* Non-blocking connect */
1449		if (connect_type & PGSQL_CONNECT_ASYNC) {
1450			if (connstring) {
1451				pgsql = PQconnectStart(connstring);
1452				if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1453					PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1454					if (pgsql) {
1455						PQfinish(pgsql);
1456					}
1457					goto err;
1458				}
1459			} else {
1460				php_error_docref(NULL, E_WARNING, "Connection string required for async connections");
1461				goto err;
1462			}
1463		} else {
1464			if (connstring) {
1465				pgsql = PQconnectdb(connstring);
1466			} else {
1467				pgsql = PQsetdb(host,port,options,tty,dbname);
1468			}
1469			if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1470				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1471				if (pgsql) {
1472					PQfinish(pgsql);
1473				}
1474				goto err;
1475			}
1476		}
1477
1478		/* add it to the list */
1479		RETVAL_RES(zend_register_resource(pgsql, le_link));
1480
1481		/* add it to the hash */
1482		new_index_ptr.ptr = (void *) Z_RES_P(return_value);
1483		new_index_ptr.type = le_index_ptr;
1484		if (zend_hash_update_mem(&EG(regular_list), str.s, (void *) &new_index_ptr, sizeof(zend_resource)) == NULL) {
1485			goto err;
1486		}
1487		PGG(num_links)++;
1488	}
1489	/* set notice processor */
1490	if (! PGG(ignore_notices) && Z_TYPE_P(return_value) == IS_RESOURCE) {
1491		PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, (void*)(zend_uintptr_t)Z_RES_HANDLE_P(return_value));
1492	}
1493	php_pgsql_set_default_link(Z_RES_P(return_value));
1494
1495cleanup:
1496	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1497		zval_dtor(&args[i]);
1498	}
1499	efree(args);
1500	smart_str_free(&str);
1501	return;
1502
1503err:
1504	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1505		zval_dtor(&args[i]);
1506	}
1507	efree(args);
1508	smart_str_free(&str);
1509	RETURN_FALSE;
1510}
1511/* }}} */
1512
1513#if 0
1514/* {{{ php_pgsql_get_default_link
1515 */
1516static int php_pgsql_get_default_link(INTERNAL_FUNCTION_PARAMETERS)
1517{
1518	if (PGG(default_link)==-1) { /* no link opened yet, implicitly open one */
1519		ht = 0;
1520		php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
1521	}
1522	return PGG(default_link);
1523}
1524/* }}} */
1525#endif
1526
1527/* {{{ proto resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)
1528   Open a PostgreSQL connection */
1529PHP_FUNCTION(pg_connect)
1530{
1531	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
1532}
1533/* }}} */
1534
1535/* {{{ proto resource pg_connect_poll(resource connection)
1536   Poll the status of an in-progress async PostgreSQL connection attempt*/
1537PHP_FUNCTION(pg_connect_poll)
1538{
1539	zval *pgsql_link;
1540	PGconn *pgsql;
1541	int ret;
1542
1543	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
1544		return;
1545	}
1546
1547	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
1548		RETURN_FALSE;
1549	}
1550
1551	ret = PQconnectPoll(pgsql);
1552
1553	RETURN_LONG(ret);
1554}
1555/* }}} */
1556
1557/* {{{ proto resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)
1558   Open a persistent PostgreSQL connection */
1559PHP_FUNCTION(pg_pconnect)
1560{
1561	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,1);
1562}
1563/* }}} */
1564
1565/* {{{ proto bool pg_close([resource connection])
1566   Close a PostgreSQL connection */
1567PHP_FUNCTION(pg_close)
1568{
1569	zval *pgsql_link = NULL;
1570	zend_resource *link;
1571	int argc = ZEND_NUM_ARGS();
1572	PGconn *pgsql;
1573
1574	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
1575		return;
1576	}
1577
1578	if (argc == 0) {
1579		link = FETCH_DEFAULT_LINK();
1580		CHECK_DEFAULT_LINK(link);
1581	} else {
1582		link = Z_RES_P(pgsql_link);
1583	}
1584
1585	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1586		RETURN_FALSE;
1587	}
1588
1589	if (argc == 0) { /* explicit resource number */
1590		zend_list_close(link);
1591	}
1592
1593	if (argc || (pgsql_link && Z_RES_P(pgsql_link) == PGG(default_link))) {
1594		zend_list_close(link);
1595		PGG(default_link) = NULL;
1596	}
1597
1598	RETURN_TRUE;
1599}
1600/* }}} */
1601
1602#define PHP_PG_DBNAME 1
1603#define PHP_PG_ERROR_MESSAGE 2
1604#define PHP_PG_OPTIONS 3
1605#define PHP_PG_PORT 4
1606#define PHP_PG_TTY 5
1607#define PHP_PG_HOST 6
1608#define PHP_PG_VERSION 7
1609
1610/* {{{ php_pgsql_get_link_info
1611 */
1612static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
1613{
1614	zend_resource *link;
1615	zval *pgsql_link = NULL;
1616	int argc = ZEND_NUM_ARGS();
1617	PGconn *pgsql;
1618	char *msgbuf;
1619	char *result;
1620
1621	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
1622		return;
1623	}
1624
1625	if (argc == 0) {
1626		link = FETCH_DEFAULT_LINK();
1627		CHECK_DEFAULT_LINK(link);
1628	} else {
1629		link = Z_RES_P(pgsql_link);
1630	}
1631
1632	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1633		RETURN_FALSE;
1634	}
1635
1636	switch(entry_type) {
1637		case PHP_PG_DBNAME:
1638			result = PQdb(pgsql);
1639			break;
1640		case PHP_PG_ERROR_MESSAGE:
1641			result = PQErrorMessageTrim(pgsql, &msgbuf);
1642			RETVAL_STRING(result);
1643			efree(result);
1644			return;
1645		case PHP_PG_OPTIONS:
1646			result = PQoptions(pgsql);
1647			break;
1648		case PHP_PG_PORT:
1649			result = PQport(pgsql);
1650			break;
1651		case PHP_PG_TTY:
1652			result = PQtty(pgsql);
1653			break;
1654		case PHP_PG_HOST:
1655			result = PQhost(pgsql);
1656			break;
1657		case PHP_PG_VERSION:
1658			array_init(return_value);
1659			add_assoc_string(return_value, "client", PG_VERSION);
1660#if HAVE_PQPROTOCOLVERSION
1661			add_assoc_long(return_value, "protocol", PQprotocolVersion(pgsql));
1662#if HAVE_PQPARAMETERSTATUS
1663			if (PQprotocolVersion(pgsql) >= 3) {
1664				/* 8.0 or grater supports protorol version 3 */
1665				char *tmp;
1666				add_assoc_string(return_value, "server", (char*)PQparameterStatus(pgsql, "server_version"));
1667				tmp = (char*)PQparameterStatus(pgsql, "server_encoding");
1668				add_assoc_string(return_value, "server_encoding", tmp);
1669				tmp = (char*)PQparameterStatus(pgsql, "client_encoding");
1670				add_assoc_string(return_value, "client_encoding", tmp);
1671				tmp = (char*)PQparameterStatus(pgsql, "is_superuser");
1672				add_assoc_string(return_value, "is_superuser", tmp);
1673				tmp = (char*)PQparameterStatus(pgsql, "session_authorization");
1674				add_assoc_string(return_value, "session_authorization", tmp);
1675				tmp = (char*)PQparameterStatus(pgsql, "DateStyle");
1676				add_assoc_string(return_value, "DateStyle", tmp);
1677				tmp = (char*)PQparameterStatus(pgsql, "IntervalStyle");
1678				add_assoc_string(return_value, "IntervalStyle", tmp ? tmp : "");
1679				tmp = (char*)PQparameterStatus(pgsql, "TimeZone");
1680				add_assoc_string(return_value, "TimeZone", tmp ? tmp : "");
1681				tmp = (char*)PQparameterStatus(pgsql, "integer_datetimes");
1682				add_assoc_string(return_value, "integer_datetimes", tmp ? tmp : "");
1683				tmp = (char*)PQparameterStatus(pgsql, "standard_conforming_strings");
1684				add_assoc_string(return_value, "standard_conforming_strings", tmp ? tmp : "");
1685				tmp = (char*)PQparameterStatus(pgsql, "application_name");
1686				add_assoc_string(return_value, "application_name", tmp ? tmp : "");
1687			}
1688#endif
1689#endif
1690			return;
1691		default:
1692			RETURN_FALSE;
1693	}
1694	if (result) {
1695		RETURN_STRING(result);
1696	} else {
1697		RETURN_EMPTY_STRING();
1698	}
1699}
1700/* }}} */
1701
1702/* {{{ proto string pg_dbname([resource connection])
1703   Get the database name */
1704PHP_FUNCTION(pg_dbname)
1705{
1706	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_DBNAME);
1707}
1708/* }}} */
1709
1710/* {{{ proto string pg_last_error([resource connection])
1711   Get the error message string */
1712PHP_FUNCTION(pg_last_error)
1713{
1714	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_ERROR_MESSAGE);
1715}
1716/* }}} */
1717
1718/* {{{ proto string pg_options([resource connection])
1719   Get the options associated with the connection */
1720PHP_FUNCTION(pg_options)
1721{
1722	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_OPTIONS);
1723}
1724/* }}} */
1725
1726/* {{{ proto int pg_port([resource connection])
1727   Return the port number associated with the connection */
1728PHP_FUNCTION(pg_port)
1729{
1730	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_PORT);
1731}
1732/* }}} */
1733
1734/* {{{ proto string pg_tty([resource connection])
1735   Return the tty name associated with the connection */
1736PHP_FUNCTION(pg_tty)
1737{
1738	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_TTY);
1739}
1740/* }}} */
1741
1742/* {{{ proto string pg_host([resource connection])
1743   Returns the host name associated with the connection */
1744PHP_FUNCTION(pg_host)
1745{
1746	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_HOST);
1747}
1748/* }}} */
1749
1750/* {{{ proto array pg_version([resource connection])
1751   Returns an array with client, protocol and server version (when available) */
1752PHP_FUNCTION(pg_version)
1753{
1754	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_VERSION);
1755}
1756/* }}} */
1757
1758#if HAVE_PQPARAMETERSTATUS
1759/* {{{ proto string|false pg_parameter_status([resource connection,] string param_name)
1760   Returns the value of a server parameter */
1761PHP_FUNCTION(pg_parameter_status)
1762{
1763	zval *pgsql_link = NULL;
1764	zend_resource *link;
1765	PGconn *pgsql;
1766	char *param;
1767	size_t len;
1768
1769	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rs", &pgsql_link, &param, &len) == FAILURE) {
1770		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &param, &len) == SUCCESS) {
1771			link = FETCH_DEFAULT_LINK();
1772			CHECK_DEFAULT_LINK(link);
1773		} else {
1774			RETURN_FALSE;
1775		}
1776	} else {
1777		link = Z_RES_P(pgsql_link);
1778	}
1779
1780	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1781		RETURN_FALSE;
1782	}
1783
1784	param = (char*)PQparameterStatus(pgsql, param);
1785	if (param) {
1786		RETURN_STRING(param);
1787	} else {
1788		RETURN_FALSE;
1789	}
1790}
1791/* }}} */
1792#endif
1793
1794/* {{{ proto bool pg_ping([resource connection])
1795   Ping database. If connection is bad, try to reconnect. */
1796PHP_FUNCTION(pg_ping)
1797{
1798	zval *pgsql_link;
1799	PGconn *pgsql;
1800	PGresult *res;
1801	zend_resource *link;
1802
1803	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == SUCCESS) {
1804		link = Z_RES_P(pgsql_link);
1805	} else {
1806		link = FETCH_DEFAULT_LINK();
1807		CHECK_DEFAULT_LINK(link);
1808	}
1809
1810	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1811		RETURN_FALSE;
1812	}
1813
1814	/* ping connection */
1815	res = PQexec(pgsql, "SELECT 1;");
1816	PQclear(res);
1817
1818	/* check status. */
1819	if (PQstatus(pgsql) == CONNECTION_OK)
1820		RETURN_TRUE;
1821
1822	/* reset connection if it's broken */
1823	PQreset(pgsql);
1824	if (PQstatus(pgsql) == CONNECTION_OK) {
1825		RETURN_TRUE;
1826	}
1827	RETURN_FALSE;
1828}
1829/* }}} */
1830
1831/* {{{ proto resource pg_query([resource connection,] string query)
1832   Execute a query */
1833PHP_FUNCTION(pg_query)
1834{
1835	zval *pgsql_link = NULL;
1836	char *query;
1837	int  argc = ZEND_NUM_ARGS();
1838	size_t query_len;
1839	int leftover = 0;
1840	zend_resource *link;
1841	PGconn *pgsql;
1842	PGresult *pgsql_result;
1843	ExecStatusType status;
1844	pgsql_result_handle *pg_result;
1845
1846	if (argc == 1) {
1847		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
1848			return;
1849		}
1850		link = FETCH_DEFAULT_LINK();
1851		CHECK_DEFAULT_LINK(link);
1852	} else {
1853		if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &query_len) == FAILURE) {
1854			return;
1855		}
1856		link = Z_RES_P(pgsql_link);
1857	}
1858
1859	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1860		RETURN_FALSE;
1861	}
1862
1863	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1864		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1865		RETURN_FALSE;
1866	}
1867	while ((pgsql_result = PQgetResult(pgsql))) {
1868		PQclear(pgsql_result);
1869		leftover = 1;
1870	}
1871	if (leftover) {
1872		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1873	}
1874	pgsql_result = PQexec(pgsql, query);
1875	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1876		PQclear(pgsql_result);
1877		PQreset(pgsql);
1878		pgsql_result = PQexec(pgsql, query);
1879	}
1880
1881	if (pgsql_result) {
1882		status = PQresultStatus(pgsql_result);
1883	} else {
1884		status = (ExecStatusType) PQstatus(pgsql);
1885	}
1886
1887	switch (status) {
1888		case PGRES_EMPTY_QUERY:
1889		case PGRES_BAD_RESPONSE:
1890		case PGRES_NONFATAL_ERROR:
1891		case PGRES_FATAL_ERROR:
1892			PHP_PQ_ERROR("Query failed: %s", pgsql);
1893			PQclear(pgsql_result);
1894			RETURN_FALSE;
1895			break;
1896		case PGRES_COMMAND_OK: /* successful command that did not return rows */
1897		default:
1898			if (pgsql_result) {
1899				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
1900				pg_result->conn = pgsql;
1901				pg_result->result = pgsql_result;
1902				pg_result->row = 0;
1903				RETURN_RES(zend_register_resource(pg_result, le_result));
1904			} else {
1905				PQclear(pgsql_result);
1906				RETURN_FALSE;
1907			}
1908			break;
1909	}
1910}
1911/* }}} */
1912
1913#if HAVE_PQEXECPARAMS || HAVE_PQEXECPREPARED || HAVE_PQSENDQUERYPARAMS || HAVE_PQSENDQUERYPREPARED
1914/* {{{ _php_pgsql_free_params */
1915static void _php_pgsql_free_params(char **params, int num_params)
1916{
1917	if (num_params > 0) {
1918		int i;
1919		for (i = 0; i < num_params; i++) {
1920			if (params[i]) {
1921				efree(params[i]);
1922			}
1923		}
1924		efree(params);
1925	}
1926}
1927/* }}} */
1928#endif
1929
1930#if HAVE_PQEXECPARAMS
1931/* {{{ proto resource pg_query_params([resource connection,] string query, array params)
1932   Execute a query */
1933PHP_FUNCTION(pg_query_params)
1934{
1935	zval *pgsql_link = NULL;
1936	zval *pv_param_arr, *tmp;
1937	char *query;
1938	size_t query_len;
1939	int argc = ZEND_NUM_ARGS();
1940	int leftover = 0;
1941	int num_params = 0;
1942	char **params = NULL;
1943	zend_resource *link;
1944	PGconn *pgsql;
1945	PGresult *pgsql_result;
1946	ExecStatusType status;
1947	pgsql_result_handle *pg_result;
1948
1949	if (argc == 2) {
1950		if (zend_parse_parameters(argc, "sa", &query, &query_len, &pv_param_arr) == FAILURE) {
1951			return;
1952		}
1953		link = FETCH_DEFAULT_LINK();
1954		CHECK_DEFAULT_LINK(link);
1955	} else {
1956		if (zend_parse_parameters(argc, "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
1957			return;
1958		}
1959		link = Z_RES_P(pgsql_link);
1960	}
1961
1962	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1963		RETURN_FALSE;
1964	}
1965
1966	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1967		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1968		RETURN_FALSE;
1969	}
1970	while ((pgsql_result = PQgetResult(pgsql))) {
1971		PQclear(pgsql_result);
1972		leftover = 1;
1973	}
1974	if (leftover) {
1975		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1976	}
1977
1978	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
1979	if (num_params > 0) {
1980		int i = 0;
1981		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
1982
1983		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
1984			ZVAL_DEREF(tmp);
1985			if (Z_TYPE_P(tmp) == IS_NULL) {
1986				params[i] = NULL;
1987			} else {
1988				zval tmp_val;
1989
1990				ZVAL_COPY(&tmp_val, tmp);
1991				convert_to_cstring(&tmp_val);
1992				if (Z_TYPE(tmp_val) != IS_STRING) {
1993					php_error_docref(NULL, E_WARNING,"Error converting parameter");
1994					zval_ptr_dtor(&tmp_val);
1995					_php_pgsql_free_params(params, num_params);
1996					RETURN_FALSE;
1997				}
1998				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
1999				zval_ptr_dtor(&tmp_val);
2000			}
2001			i++;
2002		} ZEND_HASH_FOREACH_END();
2003	}
2004
2005	pgsql_result = PQexecParams(pgsql, query, num_params,
2006					NULL, (const char * const *)params, NULL, NULL, 0);
2007	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2008		PQclear(pgsql_result);
2009		PQreset(pgsql);
2010		pgsql_result = PQexecParams(pgsql, query, num_params,
2011						NULL, (const char * const *)params, NULL, NULL, 0);
2012	}
2013
2014	if (pgsql_result) {
2015		status = PQresultStatus(pgsql_result);
2016	} else {
2017		status = (ExecStatusType) PQstatus(pgsql);
2018	}
2019
2020	_php_pgsql_free_params(params, num_params);
2021
2022	switch (status) {
2023		case PGRES_EMPTY_QUERY:
2024		case PGRES_BAD_RESPONSE:
2025		case PGRES_NONFATAL_ERROR:
2026		case PGRES_FATAL_ERROR:
2027			PHP_PQ_ERROR("Query failed: %s", pgsql);
2028			PQclear(pgsql_result);
2029			RETURN_FALSE;
2030			break;
2031		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2032		default:
2033			if (pgsql_result) {
2034				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2035				pg_result->conn = pgsql;
2036				pg_result->result = pgsql_result;
2037				pg_result->row = 0;
2038				RETURN_RES(zend_register_resource(pg_result, le_result));
2039			} else {
2040				PQclear(pgsql_result);
2041				RETURN_FALSE;
2042			}
2043			break;
2044	}
2045}
2046/* }}} */
2047#endif
2048
2049#if HAVE_PQPREPARE
2050/* {{{ proto resource pg_prepare([resource connection,] string stmtname, string query)
2051   Prepare a query for future execution */
2052PHP_FUNCTION(pg_prepare)
2053{
2054	zval *pgsql_link = NULL;
2055	char *query, *stmtname;
2056	size_t query_len, stmtname_len;
2057	int argc = ZEND_NUM_ARGS();
2058	int leftover = 0;
2059	PGconn *pgsql;
2060	zend_resource *link;
2061	PGresult *pgsql_result;
2062	ExecStatusType status;
2063	pgsql_result_handle *pg_result;
2064
2065	if (argc == 2) {
2066		if (zend_parse_parameters(argc, "ss", &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2067			return;
2068		}
2069		link = FETCH_DEFAULT_LINK();
2070		CHECK_DEFAULT_LINK(link);
2071	} else {
2072		if (zend_parse_parameters(argc, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2073			return;
2074		}
2075		link = Z_RES_P(pgsql_link);
2076	}
2077
2078	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2079		RETURN_FALSE;
2080	}
2081
2082	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2083		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2084		RETURN_FALSE;
2085	}
2086	while ((pgsql_result = PQgetResult(pgsql))) {
2087		PQclear(pgsql_result);
2088		leftover = 1;
2089	}
2090	if (leftover) {
2091		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2092	}
2093	pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2094	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2095		PQclear(pgsql_result);
2096		PQreset(pgsql);
2097		pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2098	}
2099
2100	if (pgsql_result) {
2101		status = PQresultStatus(pgsql_result);
2102	} else {
2103		status = (ExecStatusType) PQstatus(pgsql);
2104	}
2105
2106	switch (status) {
2107		case PGRES_EMPTY_QUERY:
2108		case PGRES_BAD_RESPONSE:
2109		case PGRES_NONFATAL_ERROR:
2110		case PGRES_FATAL_ERROR:
2111			PHP_PQ_ERROR("Query failed: %s", pgsql);
2112			PQclear(pgsql_result);
2113			RETURN_FALSE;
2114			break;
2115		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2116		default:
2117			if (pgsql_result) {
2118				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2119				pg_result->conn = pgsql;
2120				pg_result->result = pgsql_result;
2121				pg_result->row = 0;
2122				RETURN_RES(zend_register_resource(pg_result, le_result));
2123			} else {
2124				PQclear(pgsql_result);
2125				RETURN_FALSE;
2126			}
2127			break;
2128	}
2129}
2130/* }}} */
2131#endif
2132
2133#if HAVE_PQEXECPREPARED
2134/* {{{ proto resource pg_execute([resource connection,] string stmtname, array params)
2135   Execute a prepared query  */
2136PHP_FUNCTION(pg_execute)
2137{
2138	zval *pgsql_link = NULL;
2139	zval *pv_param_arr, *tmp;
2140	char *stmtname;
2141	size_t stmtname_len;
2142	int argc = ZEND_NUM_ARGS();
2143	int leftover = 0;
2144	int num_params = 0;
2145	char **params = NULL;
2146	PGconn *pgsql;
2147	zend_resource *link;
2148	PGresult *pgsql_result;
2149	ExecStatusType status;
2150	pgsql_result_handle *pg_result;
2151
2152	if (argc == 2) {
2153		if (zend_parse_parameters(argc, "sa/", &stmtname, &stmtname_len, &pv_param_arr)==FAILURE) {
2154			return;
2155		}
2156		link = FETCH_DEFAULT_LINK();
2157		CHECK_DEFAULT_LINK(link);
2158	} else {
2159		if (zend_parse_parameters(argc, "rsa/", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
2160			return;
2161		}
2162		link = Z_RES_P(pgsql_link);
2163	}
2164
2165	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2166		RETURN_FALSE;
2167	}
2168
2169	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2170		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2171		RETURN_FALSE;
2172	}
2173	while ((pgsql_result = PQgetResult(pgsql))) {
2174		PQclear(pgsql_result);
2175		leftover = 1;
2176	}
2177	if (leftover) {
2178		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2179	}
2180
2181	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
2182	if (num_params > 0) {
2183		int i = 0;
2184		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
2185
2186		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
2187
2188			if (Z_TYPE_P(tmp) == IS_NULL) {
2189				params[i] = NULL;
2190			} else {
2191				zval tmp_val;
2192
2193				ZVAL_COPY(&tmp_val, tmp);
2194				convert_to_string(&tmp_val);
2195				if (Z_TYPE(tmp_val) != IS_STRING) {
2196					php_error_docref(NULL, E_WARNING,"Error converting parameter");
2197					zval_ptr_dtor(&tmp_val);
2198					_php_pgsql_free_params(params, num_params);
2199					RETURN_FALSE;
2200				}
2201				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
2202				zval_ptr_dtor(&tmp_val);
2203			}
2204
2205			i++;
2206		} ZEND_HASH_FOREACH_END();
2207	}
2208
2209	pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2210					(const char * const *)params, NULL, NULL, 0);
2211	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2212		PQclear(pgsql_result);
2213		PQreset(pgsql);
2214		pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2215						(const char * const *)params, NULL, NULL, 0);
2216	}
2217
2218	if (pgsql_result) {
2219		status = PQresultStatus(pgsql_result);
2220	} else {
2221		status = (ExecStatusType) PQstatus(pgsql);
2222	}
2223
2224	_php_pgsql_free_params(params, num_params);
2225
2226	switch (status) {
2227		case PGRES_EMPTY_QUERY:
2228		case PGRES_BAD_RESPONSE:
2229		case PGRES_NONFATAL_ERROR:
2230		case PGRES_FATAL_ERROR:
2231			PHP_PQ_ERROR("Query failed: %s", pgsql);
2232			PQclear(pgsql_result);
2233			RETURN_FALSE;
2234			break;
2235		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2236		default:
2237			if (pgsql_result) {
2238				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2239				pg_result->conn = pgsql;
2240				pg_result->result = pgsql_result;
2241				pg_result->row = 0;
2242				RETURN_RES(zend_register_resource(pg_result, le_result));
2243			} else {
2244				PQclear(pgsql_result);
2245				RETURN_FALSE;
2246			}
2247			break;
2248	}
2249}
2250/* }}} */
2251#endif
2252
2253#define PHP_PG_NUM_ROWS 1
2254#define PHP_PG_NUM_FIELDS 2
2255#define PHP_PG_CMD_TUPLES 3
2256
2257/* {{{ php_pgsql_get_result_info
2258 */
2259static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2260{
2261	zval *result;
2262	PGresult *pgsql_result;
2263	pgsql_result_handle *pg_result;
2264
2265	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
2266		return;
2267	}
2268
2269	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2270		RETURN_FALSE;
2271	}
2272
2273	pgsql_result = pg_result->result;
2274
2275	switch (entry_type) {
2276		case PHP_PG_NUM_ROWS:
2277			RETVAL_LONG(PQntuples(pgsql_result));
2278			break;
2279		case PHP_PG_NUM_FIELDS:
2280			RETVAL_LONG(PQnfields(pgsql_result));
2281			break;
2282		case PHP_PG_CMD_TUPLES:
2283#if HAVE_PQCMDTUPLES
2284			RETVAL_LONG(atoi(PQcmdTuples(pgsql_result)));
2285#else
2286			php_error_docref(NULL, E_WARNING, "Not supported under this build");
2287			RETVAL_LONG(0);
2288#endif
2289			break;
2290		default:
2291			RETURN_FALSE;
2292	}
2293}
2294/* }}} */
2295
2296/* {{{ proto int pg_num_rows(resource result)
2297   Return the number of rows in the result */
2298PHP_FUNCTION(pg_num_rows)
2299{
2300	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_ROWS);
2301}
2302/* }}} */
2303
2304/* {{{ proto int pg_num_fields(resource result)
2305   Return the number of fields in the result */
2306PHP_FUNCTION(pg_num_fields)
2307{
2308	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_FIELDS);
2309}
2310/* }}} */
2311
2312#if HAVE_PQCMDTUPLES
2313/* {{{ proto int pg_affected_rows(resource result)
2314   Returns the number of affected tuples */
2315PHP_FUNCTION(pg_affected_rows)
2316{
2317	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_CMD_TUPLES);
2318}
2319/* }}} */
2320#endif
2321
2322/* {{{ proto mixed pg_last_notice(resource connection [, long option])
2323   Returns the last notice set by the backend */
2324PHP_FUNCTION(pg_last_notice)
2325{
2326	zval *pgsql_link = NULL;
2327	zval *notice, *notices;
2328	PGconn *pg_link;
2329	zend_long option = PGSQL_NOTICE_LAST;
2330
2331	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pgsql_link, &option) == FAILURE) {
2332		return;
2333	}
2334
2335	/* Just to check if user passed valid resoruce */
2336	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
2337		RETURN_FALSE;
2338	}
2339
2340	notices = zend_hash_index_find(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link));
2341	switch (option) {
2342		case PGSQL_NOTICE_LAST:
2343			if (notices) {
2344				zend_hash_internal_pointer_end(Z_ARRVAL_P(notices));
2345				if ((notice = zend_hash_get_current_data(Z_ARRVAL_P(notices))) == NULL) {
2346					RETURN_EMPTY_STRING();
2347				}
2348				RETURN_ZVAL(notice, 1, 0);
2349			} else {
2350				RETURN_EMPTY_STRING();
2351			}
2352			break;
2353		case PGSQL_NOTICE_ALL:
2354			if (notices) {
2355				RETURN_ZVAL(notices, 1, 0);
2356			} else {
2357				array_init(return_value);
2358				return;
2359			}
2360			break;
2361		case PGSQL_NOTICE_CLEAR:
2362			if (notices) {
2363				zend_hash_clean(&PGG(notices));
2364			}
2365			RETURN_TRUE;
2366			break;
2367		default:
2368			php_error_docref(NULL, E_WARNING,
2369				"Invalid option specified (" ZEND_LONG_FMT ")", option);
2370	}
2371	RETURN_FALSE;
2372}
2373/* }}} */
2374
2375/* {{{ get_field_name
2376 */
2377static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list)
2378{
2379	PGresult *result;
2380	smart_str str = {0};
2381	zend_resource *field_type;
2382	char *ret=NULL;
2383
2384	/* try to lookup the type in the resource list */
2385	smart_str_appends(&str, "pgsql_oid_");
2386	smart_str_append_unsigned(&str, oid);
2387	smart_str_0(&str);
2388
2389	if ((field_type = zend_hash_find_ptr(list, str.s)) != NULL) {
2390		ret = estrdup((char *)field_type->ptr);
2391	} else { /* hash all oid's */
2392		int i, num_rows;
2393		int oid_offset,name_offset;
2394		char *tmp_oid, *end_ptr, *tmp_name;
2395		zend_resource new_oid_entry;
2396
2397		if ((result = PQexec(pgsql, "select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
2398			if (result) {
2399				PQclear(result);
2400			}
2401			smart_str_free(&str);
2402			return estrndup("", sizeof("") - 1);
2403		}
2404		num_rows = PQntuples(result);
2405		oid_offset = PQfnumber(result,"oid");
2406		name_offset = PQfnumber(result,"typname");
2407
2408		for (i=0; i<num_rows; i++) {
2409			if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
2410				continue;
2411			}
2412
2413			smart_str_free(&str);
2414			smart_str_appends(&str, "pgsql_oid_");
2415			smart_str_appends(&str, tmp_oid);
2416			smart_str_0(&str);
2417
2418			if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
2419				continue;
2420			}
2421			new_oid_entry.type = le_string;
2422			new_oid_entry.ptr = estrdup(tmp_name);
2423			zend_hash_update_mem(list, str.s, (void *) &new_oid_entry, sizeof(zend_resource));
2424			if (!ret && strtoul(tmp_oid, &end_ptr, 10)==oid) {
2425				ret = estrdup(tmp_name);
2426			}
2427		}
2428		PQclear(result);
2429	}
2430
2431	smart_str_free(&str);
2432	return ret;
2433}
2434/* }}} */
2435
2436#ifdef HAVE_PQFTABLE
2437/* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only])
2438   Returns the name of the table field belongs to, or table's oid if oid_only is true */
2439PHP_FUNCTION(pg_field_table)
2440{
2441	zval *result;
2442	pgsql_result_handle *pg_result;
2443	zend_long fnum = -1;
2444	zend_bool return_oid = 0;
2445	Oid oid;
2446	smart_str hash_key = {0};
2447	char *table_name;
2448	zend_resource *field_table;
2449
2450	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|b", &result, &fnum, &return_oid) == FAILURE) {
2451		return;
2452	}
2453
2454	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2455		RETURN_FALSE;
2456	}
2457
2458	if (fnum < 0 || fnum >= PQnfields(pg_result->result)) {
2459		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2460		RETURN_FALSE;
2461	}
2462
2463	oid = PQftable(pg_result->result, (int)fnum);
2464
2465	if (InvalidOid == oid) {
2466		RETURN_FALSE;
2467	}
2468
2469	if (return_oid) {
2470#if UINT_MAX > ZEND_LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */
2471		if (oid > ZEND_LONG_MAX) {
2472			smart_str oidstr = {0};
2473			smart_str_append_unsigned(&oidstr, oid);
2474			smart_str_0(&oidstr);
2475			RETURN_NEW_STR(oidstr.s);
2476		} else
2477#endif
2478			RETURN_LONG((zend_long)oid);
2479	}
2480
2481	/* try to lookup the table name in the resource list */
2482	smart_str_appends(&hash_key, "pgsql_table_oid_");
2483	smart_str_append_unsigned(&hash_key, oid);
2484	smart_str_0(&hash_key);
2485
2486	if ((field_table = zend_hash_find_ptr(&EG(regular_list), hash_key.s)) != NULL) {
2487		smart_str_free(&hash_key);
2488		RETURN_STRING((char *)field_table->ptr);
2489	} else { /* Not found, lookup by querying PostgreSQL system tables */
2490		PGresult *tmp_res;
2491		smart_str querystr = {0};
2492		zend_resource new_field_table;
2493
2494		smart_str_appends(&querystr, "select relname from pg_class where oid=");
2495		smart_str_append_unsigned(&querystr, oid);
2496		smart_str_0(&querystr);
2497
2498		if ((tmp_res = PQexec(pg_result->conn, ZSTR_VAL(querystr.s))) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
2499			if (tmp_res) {
2500				PQclear(tmp_res);
2501			}
2502			smart_str_free(&querystr);
2503			smart_str_free(&hash_key);
2504			RETURN_FALSE;
2505		}
2506
2507		smart_str_free(&querystr);
2508
2509		if ((table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
2510			PQclear(tmp_res);
2511			smart_str_free(&hash_key);
2512			RETURN_FALSE;
2513		}
2514
2515		new_field_table.type = le_string;
2516		new_field_table.ptr = estrdup(table_name);
2517		zend_hash_update_mem(&EG(regular_list), hash_key.s, (void *)&new_field_table, sizeof(zend_resource));
2518
2519		smart_str_free(&hash_key);
2520		PQclear(tmp_res);
2521		RETURN_STRING(table_name);
2522	}
2523
2524}
2525/* }}} */
2526#endif
2527
2528#define PHP_PG_FIELD_NAME 1
2529#define PHP_PG_FIELD_SIZE 2
2530#define PHP_PG_FIELD_TYPE 3
2531#define PHP_PG_FIELD_TYPE_OID 4
2532
2533/* {{{ php_pgsql_get_field_info
2534 */
2535static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2536{
2537	zval *result;
2538	zend_long field;
2539	PGresult *pgsql_result;
2540	pgsql_result_handle *pg_result;
2541	Oid oid;
2542
2543	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &field) == FAILURE) {
2544		return;
2545	}
2546
2547	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2548		RETURN_FALSE;
2549	}
2550
2551
2552	pgsql_result = pg_result->result;
2553
2554	if (field < 0 || field >= PQnfields(pgsql_result)) {
2555		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2556		RETURN_FALSE;
2557	}
2558
2559	switch (entry_type) {
2560		case PHP_PG_FIELD_NAME:
2561			RETURN_STRING(PQfname(pgsql_result, (int)field));
2562			break;
2563		case PHP_PG_FIELD_SIZE:
2564			RETURN_LONG(PQfsize(pgsql_result, (int)field));
2565			break;
2566		case PHP_PG_FIELD_TYPE: {
2567				char *name = get_field_name(pg_result->conn, PQftype(pgsql_result, (int)field), &EG(regular_list));
2568				RETVAL_STRING(name);
2569				efree(name);
2570			}
2571			break;
2572		case PHP_PG_FIELD_TYPE_OID:
2573
2574			oid = PQftype(pgsql_result, (int)field);
2575#if UINT_MAX > ZEND_LONG_MAX
2576			if (oid > ZEND_LONG_MAX) {
2577				smart_str s = {0};
2578				smart_str_append_unsigned(&s, oid);
2579				smart_str_0(&s);
2580				RETURN_NEW_STR(s.s);
2581			} else
2582#endif
2583			{
2584				RETURN_LONG((zend_long)oid);
2585			}
2586			break;
2587		default:
2588			RETURN_FALSE;
2589	}
2590}
2591/* }}} */
2592
2593/* {{{ proto string pg_field_name(resource result, int field_number)
2594   Returns the name of the field */
2595PHP_FUNCTION(pg_field_name)
2596{
2597	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
2598}
2599/* }}} */
2600
2601/* {{{ proto int pg_field_size(resource result, int field_number)
2602   Returns the internal size of the field */
2603PHP_FUNCTION(pg_field_size)
2604{
2605	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
2606}
2607/* }}} */
2608
2609/* {{{ proto string pg_field_type(resource result, int field_number)
2610   Returns the type name for the given field */
2611PHP_FUNCTION(pg_field_type)
2612{
2613	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
2614}
2615/* }}} */
2616
2617/* {{{ proto string pg_field_type_oid(resource result, int field_number)
2618   Returns the type oid for the given field */
2619PHP_FUNCTION(pg_field_type_oid)
2620{
2621	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
2622}
2623/* }}} */
2624
2625/* {{{ proto int pg_field_num(resource result, string field_name)
2626   Returns the field number of the named field */
2627PHP_FUNCTION(pg_field_num)
2628{
2629	zval *result;
2630	char *field;
2631	size_t field_len;
2632	PGresult *pgsql_result;
2633	pgsql_result_handle *pg_result;
2634
2635	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &result, &field, &field_len) == FAILURE) {
2636		return;
2637	}
2638
2639	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2640		RETURN_FALSE;
2641	}
2642
2643	pgsql_result = pg_result->result;
2644
2645	RETURN_LONG(PQfnumber(pgsql_result, field));
2646}
2647/* }}} */
2648
2649/* {{{ proto mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)
2650   Returns values from a result identifier */
2651PHP_FUNCTION(pg_fetch_result)
2652{
2653	zval *result, *field=NULL;
2654	zend_long row;
2655	PGresult *pgsql_result;
2656	pgsql_result_handle *pg_result;
2657	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
2658
2659	if (argc == 2) {
2660		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
2661			return;
2662		}
2663	} else {
2664		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
2665			return;
2666		}
2667	}
2668
2669	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2670		RETURN_FALSE;
2671	}
2672
2673	pgsql_result = pg_result->result;
2674	if (argc == 2) {
2675		if (pg_result->row < 0) {
2676			pg_result->row = 0;
2677		}
2678		pgsql_row = pg_result->row;
2679		if (pgsql_row >= PQntuples(pgsql_result)) {
2680			RETURN_FALSE;
2681		}
2682	} else {
2683		if (row < 0 || row >= PQntuples(pgsql_result)) {
2684			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
2685							row, Z_LVAL_P(result));
2686			RETURN_FALSE;
2687		}
2688		pgsql_row = (int)row;
2689	}
2690	switch (Z_TYPE_P(field)) {
2691		case IS_STRING:
2692			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
2693			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
2694				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2695				RETURN_FALSE;
2696			}
2697			break;
2698		default:
2699			convert_to_long_ex(field);
2700			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
2701				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2702				RETURN_FALSE;
2703			}
2704			field_offset = (int)Z_LVAL_P(field);
2705			break;
2706	}
2707
2708	if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
2709		RETVAL_NULL();
2710	} else {
2711		RETVAL_STRINGL(PQgetvalue(pgsql_result, pgsql_row, field_offset),
2712				PQgetlength(pgsql_result, pgsql_row, field_offset));
2713	}
2714}
2715/* }}} */
2716
2717/* {{{ void php_pgsql_fetch_hash */
2718static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
2719{
2720	zval                *result, *zrow = NULL;
2721	PGresult            *pgsql_result;
2722	pgsql_result_handle *pg_result;
2723	int             i, num_fields, pgsql_row, use_row;
2724	zend_long            row = -1;
2725	char            *field_name;
2726	zval            *ctor_params = NULL;
2727	zend_class_entry *ce = NULL;
2728
2729	if (into_object) {
2730		zend_string *class_name = NULL;
2731
2732		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) {
2733			return;
2734		}
2735		if (!class_name) {
2736			ce = zend_standard_class_def;
2737		} else {
2738			ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
2739		}
2740		if (!ce) {
2741			php_error_docref(NULL, E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name));
2742			return;
2743		}
2744		result_type = PGSQL_ASSOC;
2745	} else {
2746		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) {
2747			return;
2748		}
2749	}
2750	if (zrow == NULL) {
2751		row = -1;
2752	} else {
2753		convert_to_long(zrow);
2754		row = Z_LVAL_P(zrow);
2755		if (row < 0) {
2756			php_error_docref(NULL, E_WARNING, "The row parameter must be greater or equal to zero");
2757			RETURN_FALSE;
2758		}
2759	}
2760	use_row = ZEND_NUM_ARGS() > 1 && row != -1;
2761
2762	if (!(result_type & PGSQL_BOTH)) {
2763		php_error_docref(NULL, E_WARNING, "Invalid result type");
2764		RETURN_FALSE;
2765	}
2766
2767	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2768		RETURN_FALSE;
2769	}
2770
2771	pgsql_result = pg_result->result;
2772
2773	if (use_row) {
2774		if (row < 0 || row >= PQntuples(pgsql_result)) {
2775			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
2776							row, Z_LVAL_P(result));
2777			RETURN_FALSE;
2778		}
2779		pgsql_row = (int)row;
2780		pg_result->row = pgsql_row;
2781	} else {
2782		/* If 2nd param is NULL, use internal row counter to access next row */
2783		pgsql_row = pg_result->row;
2784		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
2785			RETURN_FALSE;
2786		}
2787		pg_result->row++;
2788	}
2789
2790	array_init(return_value);
2791	for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
2792		if (PQgetisnull(pgsql_result, pgsql_row, i)) {
2793			if (result_type & PGSQL_NUM) {
2794				add_index_null(return_value, i);
2795			}
2796			if (result_type & PGSQL_ASSOC) {
2797				field_name = PQfname(pgsql_result, i);
2798				add_assoc_null(return_value, field_name);
2799			}
2800		} else {
2801			char *element = PQgetvalue(pgsql_result, pgsql_row, i);
2802			if (element) {
2803				const size_t element_len = strlen(element);
2804
2805				if (result_type & PGSQL_NUM) {
2806					add_index_stringl(return_value, i, element, element_len);
2807				}
2808
2809				if (result_type & PGSQL_ASSOC) {
2810					field_name = PQfname(pgsql_result, i);
2811					add_assoc_stringl(return_value, field_name, element, element_len);
2812				}
2813			}
2814		}
2815	}
2816
2817	if (into_object) {
2818		zval dataset;
2819		zend_fcall_info fci;
2820		zend_fcall_info_cache fcc;
2821		zval retval;
2822
2823		ZVAL_COPY_VALUE(&dataset, return_value);
2824		object_and_properties_init(return_value, ce, NULL);
2825		if (!ce->default_properties_count && !ce->__set) {
2826			Z_OBJ_P(return_value)->properties = Z_ARR(dataset);
2827		} else {
2828			zend_merge_properties(return_value, Z_ARRVAL(dataset));
2829			zval_ptr_dtor(&dataset);
2830		}
2831
2832		if (ce->constructor) {
2833			fci.size = sizeof(fci);
2834			ZVAL_UNDEF(&fci.function_name);
2835			fci.object = Z_OBJ_P(return_value);
2836			fci.retval = &retval;
2837			fci.params = NULL;
2838			fci.param_count = 0;
2839			fci.no_separation = 1;
2840
2841			if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
2842				if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
2843					/* Two problems why we throw exceptions here: PHP is typeless
2844					 * and hence passing one argument that's not an array could be
2845					 * by mistake and the other way round is possible, too. The
2846					 * single value is an array. Also we'd have to make that one
2847					 * argument passed by reference.
2848					 */
2849					zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0);
2850					return;
2851				}
2852			}
2853
2854			fcc.initialized = 1;
2855			fcc.function_handler = ce->constructor;
2856			fcc.calling_scope = zend_get_executed_scope();
2857			fcc.called_scope = Z_OBJCE_P(return_value);
2858			fcc.object = Z_OBJ_P(return_value);
2859
2860			if (zend_call_function(&fci, &fcc) == FAILURE) {
2861				zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
2862			} else {
2863				zval_ptr_dtor(&retval);
2864			}
2865			if (fci.params) {
2866				efree(fci.params);
2867			}
2868		} else if (ctor_params) {
2869			zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
2870		}
2871	}
2872}
2873/* }}} */
2874
2875/* {{{ proto array pg_fetch_row(resource result [, int row [, int result_type]])
2876   Get a row as an enumerated array */
2877PHP_FUNCTION(pg_fetch_row)
2878{
2879	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
2880}
2881/* }}} */
2882
2883/* {{{ proto array pg_fetch_assoc(resource result [, int row])
2884   Fetch a row as an assoc array */
2885PHP_FUNCTION(pg_fetch_assoc)
2886{
2887	/* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
2888	   there is 3rd parameter */
2889	if (ZEND_NUM_ARGS() > 2)
2890		WRONG_PARAM_COUNT;
2891	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
2892}
2893/* }}} */
2894
2895/* {{{ proto array pg_fetch_array(resource result [, int row [, int result_type]])
2896   Fetch a row as an array */
2897PHP_FUNCTION(pg_fetch_array)
2898{
2899	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
2900}
2901/* }}} */
2902
2903/* {{{ proto object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])
2904   Fetch a row as an object */
2905PHP_FUNCTION(pg_fetch_object)
2906{
2907	/* pg_fetch_object() allowed result_type used to be. 3rd parameter
2908	   must be allowed for compatibility */
2909	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
2910}
2911/* }}} */
2912
2913/* {{{ proto array pg_fetch_all(resource result [, int result_type])
2914   Fetch all rows into array */
2915PHP_FUNCTION(pg_fetch_all)
2916{
2917	zval *result;
2918	long result_type = PGSQL_ASSOC;
2919	PGresult *pgsql_result;
2920	pgsql_result_handle *pg_result;
2921
2922	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
2923		return;
2924	}
2925
2926	if (!(result_type & PGSQL_BOTH)) {
2927		php_error_docref(NULL, E_WARNING, "Invalid result type");
2928		RETURN_FALSE;
2929	}
2930
2931	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2932		RETURN_FALSE;
2933	}
2934
2935	pgsql_result = pg_result->result;
2936	array_init(return_value);
2937	if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
2938		zval_dtor(return_value);
2939		RETURN_FALSE;
2940	}
2941}
2942/* }}} */
2943
2944/* {{{ proto array pg_fetch_all_columns(resource result [, int column_number])
2945   Fetch all rows into array */
2946PHP_FUNCTION(pg_fetch_all_columns)
2947{
2948	zval *result;
2949	PGresult *pgsql_result;
2950	pgsql_result_handle *pg_result;
2951	zend_long colno=0;
2952	int pg_numrows, pg_row;
2953	size_t num_fields;
2954
2955	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &colno) == FAILURE) {
2956		RETURN_FALSE;
2957	}
2958
2959	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2960		RETURN_FALSE;
2961	}
2962
2963	pgsql_result = pg_result->result;
2964
2965	num_fields = PQnfields(pgsql_result);
2966	if (colno >= (zend_long)num_fields || colno < 0) {
2967		php_error_docref(NULL, E_WARNING, "Invalid column number '%pd'", colno);
2968		RETURN_FALSE;
2969	}
2970
2971	array_init(return_value);
2972
2973	if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
2974		return;
2975	}
2976
2977	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
2978		if (PQgetisnull(pgsql_result, pg_row, (int)colno)) {
2979			add_next_index_null(return_value);
2980		} else {
2981			add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, (int)colno));
2982		}
2983	}
2984}
2985/* }}} */
2986
2987/* {{{ proto bool pg_result_seek(resource result, int offset)
2988   Set internal row offset */
2989PHP_FUNCTION(pg_result_seek)
2990{
2991	zval *result;
2992	zend_long row;
2993	pgsql_result_handle *pg_result;
2994
2995	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &row) == FAILURE) {
2996		return;
2997	}
2998
2999	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3000		RETURN_FALSE;
3001	}
3002
3003	if (row < 0 || row >= PQntuples(pg_result->result)) {
3004		RETURN_FALSE;
3005	}
3006
3007	/* seek to offset */
3008	pg_result->row = (int)row;
3009	RETURN_TRUE;
3010}
3011/* }}} */
3012
3013#define PHP_PG_DATA_LENGTH 1
3014#define PHP_PG_DATA_ISNULL 2
3015
3016/* {{{ php_pgsql_data_info
3017 */
3018static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
3019{
3020	zval *result, *field;
3021	zend_long row;
3022	PGresult *pgsql_result;
3023	pgsql_result_handle *pg_result;
3024	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
3025
3026	if (argc == 2) {
3027		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
3028			return;
3029		}
3030	} else {
3031		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
3032			return;
3033		}
3034	}
3035
3036	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3037		RETURN_FALSE;
3038	}
3039
3040	pgsql_result = pg_result->result;
3041	if (argc == 2) {
3042		if (pg_result->row < 0) {
3043			pg_result->row = 0;
3044		}
3045		pgsql_row = pg_result->row;
3046		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
3047			RETURN_FALSE;
3048		}
3049	} else {
3050		if (row < 0 || row >= PQntuples(pgsql_result)) {
3051			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
3052							row, Z_LVAL_P(result));
3053			RETURN_FALSE;
3054		}
3055		pgsql_row = (int)row;
3056	}
3057
3058	switch (Z_TYPE_P(field)) {
3059		case IS_STRING:
3060			convert_to_string_ex(field);
3061			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
3062			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
3063				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3064				RETURN_FALSE;
3065			}
3066			break;
3067		default:
3068			convert_to_long_ex(field);
3069			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
3070				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3071				RETURN_FALSE;
3072			}
3073			field_offset = (int)Z_LVAL_P(field);
3074			break;
3075	}
3076
3077	switch (entry_type) {
3078		case PHP_PG_DATA_LENGTH:
3079			RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
3080			break;
3081		case PHP_PG_DATA_ISNULL:
3082			RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset));
3083			break;
3084	}
3085}
3086/* }}} */
3087
3088/* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
3089   Returns the printed length */
3090PHP_FUNCTION(pg_field_prtlen)
3091{
3092	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
3093}
3094/* }}} */
3095
3096/* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
3097   Test if a field is NULL */
3098PHP_FUNCTION(pg_field_is_null)
3099{
3100	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
3101}
3102/* }}} */
3103
3104/* {{{ proto bool pg_free_result(resource result)
3105   Free result memory */
3106PHP_FUNCTION(pg_free_result)
3107{
3108	zval *result;
3109	pgsql_result_handle *pg_result;
3110
3111	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3112		return;
3113	}
3114
3115	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3116		RETURN_FALSE;
3117	}
3118
3119	zend_list_close(Z_RES_P(result));
3120	RETURN_TRUE;
3121}
3122/* }}} */
3123
3124/* {{{ proto string pg_last_oid(resource result)
3125   Returns the last object identifier */
3126PHP_FUNCTION(pg_last_oid)
3127{
3128	zval *result;
3129	PGresult *pgsql_result;
3130	pgsql_result_handle *pg_result;
3131#ifdef HAVE_PQOIDVALUE
3132	Oid oid;
3133#endif
3134
3135	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3136		return;
3137	}
3138
3139	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3140		RETURN_FALSE;
3141	}
3142
3143	pgsql_result = pg_result->result;
3144#ifdef HAVE_PQOIDVALUE
3145	oid = PQoidValue(pgsql_result);
3146	if (oid == InvalidOid) {
3147		RETURN_FALSE;
3148	}
3149	PGSQL_RETURN_OID(oid);
3150#else
3151	Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
3152	if (Z_STRVAL_P(return_value)) {
3153		RETURN_STRING(Z_STRVAL_P(return_value));
3154	}
3155	RETURN_EMPTY_STRING();
3156#endif
3157}
3158/* }}} */
3159
3160/* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
3161   Enable tracing a PostgreSQL connection */
3162PHP_FUNCTION(pg_trace)
3163{
3164	char *z_filename, *mode = "w";
3165	size_t z_filename_len, mode_len;
3166	zval *pgsql_link = NULL;
3167	int argc = ZEND_NUM_ARGS();
3168	PGconn *pgsql;
3169	FILE *fp = NULL;
3170	php_stream *stream;
3171	zend_resource *link;
3172
3173	if (zend_parse_parameters(argc, "p|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
3174		return;
3175	}
3176
3177	if (argc < 3) {
3178		link = FETCH_DEFAULT_LINK();
3179		CHECK_DEFAULT_LINK(link);
3180	} else {
3181		link = Z_RES_P(pgsql_link);
3182	}
3183
3184	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3185		RETURN_FALSE;
3186	}
3187
3188	stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
3189
3190	if (!stream) {
3191		RETURN_FALSE;
3192	}
3193
3194	if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS))	{
3195		php_stream_close(stream);
3196		RETURN_FALSE;
3197	}
3198	php_stream_auto_cleanup(stream);
3199	PQtrace(pgsql, fp);
3200	RETURN_TRUE;
3201}
3202/* }}} */
3203
3204/* {{{ proto bool pg_untrace([resource connection])
3205   Disable tracing of a PostgreSQL connection */
3206PHP_FUNCTION(pg_untrace)
3207{
3208	zval *pgsql_link = NULL;
3209	int argc = ZEND_NUM_ARGS();
3210	PGconn *pgsql;
3211	zend_resource *link;
3212
3213	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3214		return;
3215	}
3216
3217	if (argc == 0) {
3218		link = FETCH_DEFAULT_LINK();
3219		CHECK_DEFAULT_LINK(link);
3220	} else {
3221		link = Z_RES_P(pgsql_link);
3222	}
3223
3224	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3225		RETURN_FALSE;
3226	}
3227
3228	PQuntrace(pgsql);
3229	RETURN_TRUE;
3230}
3231/* }}} */
3232
3233/* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
3234   Create a large object */
3235PHP_FUNCTION(pg_lo_create)
3236{
3237	zval *pgsql_link = NULL, *oid = NULL;
3238	PGconn *pgsql;
3239	Oid pgsql_oid, wanted_oid = InvalidOid;
3240	int argc = ZEND_NUM_ARGS();
3241	zend_resource *link;
3242
3243	if (zend_parse_parameters(argc, "|zz", &pgsql_link, &oid) == FAILURE) {
3244		return;
3245	}
3246
3247	if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
3248		oid = pgsql_link;
3249		pgsql_link = NULL;
3250	}
3251
3252	if (pgsql_link == NULL) {
3253		link = FETCH_DEFAULT_LINK();
3254		CHECK_DEFAULT_LINK(link);
3255	} else if ((Z_TYPE_P(pgsql_link) == IS_RESOURCE)) {
3256		link = Z_RES_P(pgsql_link);
3257	} else {
3258		link = NULL;
3259	}
3260
3261	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3262		RETURN_FALSE;
3263	}
3264
3265	if (oid) {
3266#ifndef HAVE_PG_LO_CREATE
3267		php_error_docref(NULL, E_NOTICE, "Passing OID value is not supported. Upgrade your PostgreSQL");
3268#else
3269		switch (Z_TYPE_P(oid)) {
3270		case IS_STRING:
3271			{
3272				char *end_ptr;
3273				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3274				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3275				/* wrong integer format */
3276				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3277				RETURN_FALSE;
3278				}
3279			}
3280			break;
3281		case IS_LONG:
3282			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3283				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3284				RETURN_FALSE;
3285			}
3286			wanted_oid = (Oid)Z_LVAL_P(oid);
3287			break;
3288		default:
3289			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3290			RETURN_FALSE;
3291        }
3292		if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
3293			php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3294			RETURN_FALSE;
3295		}
3296
3297		PGSQL_RETURN_OID(pgsql_oid);
3298#endif
3299	}
3300
3301	if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
3302		php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3303		RETURN_FALSE;
3304	}
3305
3306	PGSQL_RETURN_OID(pgsql_oid);
3307}
3308/* }}} */
3309
3310/* {{{ proto bool pg_lo_unlink([resource connection,] string large_object_oid)
3311   Delete a large object */
3312PHP_FUNCTION(pg_lo_unlink)
3313{
3314	zval *pgsql_link = NULL;
3315	zend_long oid_long;
3316	char *oid_string, *end_ptr;
3317	size_t oid_strlen;
3318	PGconn *pgsql;
3319	Oid oid;
3320	zend_resource *link;
3321	int argc = ZEND_NUM_ARGS();
3322
3323	/* accept string type since Oid type is unsigned int */
3324	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3325								 "rs", &pgsql_link, &oid_string, &oid_strlen) == SUCCESS) {
3326		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3327		if ((oid_string+oid_strlen) != end_ptr) {
3328			/* wrong integer format */
3329			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3330			RETURN_FALSE;
3331		}
3332		link = Z_RES_P(pgsql_link);
3333	}
3334	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3335								 "rl", &pgsql_link, &oid_long) == SUCCESS) {
3336		if (oid_long <= InvalidOid) {
3337			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3338			RETURN_FALSE;
3339		}
3340		oid = (Oid)oid_long;
3341		link = Z_RES_P(pgsql_link);
3342	}
3343	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3344								 "s", &oid_string, &oid_strlen) == SUCCESS) {
3345		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3346		if ((oid_string+oid_strlen) != end_ptr) {
3347			/* wrong integer format */
3348			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3349			RETURN_FALSE;
3350		}
3351		link = FETCH_DEFAULT_LINK();
3352		CHECK_DEFAULT_LINK(link);
3353	}
3354	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3355								 "l", &oid_long) == SUCCESS) {
3356		if (oid_long <= InvalidOid) {
3357			php_error_docref(NULL, E_NOTICE, "Invalid OID is specified");
3358			RETURN_FALSE;
3359		}
3360		oid = (Oid)oid_long;
3361		link = FETCH_DEFAULT_LINK();
3362		CHECK_DEFAULT_LINK(link);
3363	}
3364	else {
3365		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3366		RETURN_FALSE;
3367	}
3368
3369	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3370		RETURN_FALSE;
3371	}
3372
3373	if (lo_unlink(pgsql, oid) == -1) {
3374		php_error_docref(NULL, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
3375		RETURN_FALSE;
3376	}
3377	RETURN_TRUE;
3378}
3379/* }}} */
3380
3381/* {{{ proto resource pg_lo_open([resource connection,] int large_object_oid, string mode)
3382   Open a large object and return fd */
3383PHP_FUNCTION(pg_lo_open)
3384{
3385	zval *pgsql_link = NULL;
3386	zend_long oid_long;
3387	char *oid_string, *end_ptr, *mode_string;
3388	size_t oid_strlen, mode_strlen;
3389	PGconn *pgsql;
3390	Oid oid;
3391	int pgsql_mode=0, pgsql_lofd;
3392	int create = 0;
3393	pgLofp *pgsql_lofp;
3394	int argc = ZEND_NUM_ARGS();
3395	zend_resource *link;
3396
3397	/* accept string type since Oid is unsigned int */
3398	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3399								 "rss", &pgsql_link, &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3400		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3401		if ((oid_string+oid_strlen) != end_ptr) {
3402			/* wrong integer format */
3403			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3404			RETURN_FALSE;
3405		}
3406		link = Z_RES_P(pgsql_link);
3407	}
3408	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3409								 "rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3410		if (oid_long <= InvalidOid) {
3411			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3412			RETURN_FALSE;
3413		}
3414		oid = (Oid)oid_long;
3415		link = Z_RES_P(pgsql_link);
3416	}
3417	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3418								 "ss", &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3419		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3420		if ((oid_string+oid_strlen) != end_ptr) {
3421			/* wrong integer format */
3422			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3423			RETURN_FALSE;
3424		}
3425		link = FETCH_DEFAULT_LINK();
3426		CHECK_DEFAULT_LINK(link);
3427	}
3428	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3429								 "ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3430		if (oid_long <= InvalidOid) {
3431			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3432			RETURN_FALSE;
3433		}
3434		oid = (Oid)oid_long;
3435		link = FETCH_DEFAULT_LINK();
3436		CHECK_DEFAULT_LINK(link);
3437	}
3438	else {
3439		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3440		RETURN_FALSE;
3441	}
3442
3443	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3444		RETURN_FALSE;
3445	}
3446
3447	/* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
3448	   faster to type. Unfortunately, doesn't behave the same way as fopen()...
3449	   (Jouni)
3450	*/
3451
3452	if (strchr(mode_string, 'r') == mode_string) {
3453		pgsql_mode |= INV_READ;
3454		if (strchr(mode_string, '+') == mode_string+1) {
3455			pgsql_mode |= INV_WRITE;
3456		}
3457	}
3458	if (strchr(mode_string, 'w') == mode_string) {
3459		pgsql_mode |= INV_WRITE;
3460		create = 1;
3461		if (strchr(mode_string, '+') == mode_string+1) {
3462			pgsql_mode |= INV_READ;
3463		}
3464	}
3465
3466	pgsql_lofp = (pgLofp *) emalloc(sizeof(pgLofp));
3467
3468	if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3469		if (create) {
3470			if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
3471				efree(pgsql_lofp);
3472				php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3473				RETURN_FALSE;
3474			} else {
3475				if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3476					if (lo_unlink(pgsql, oid) == -1) {
3477						efree(pgsql_lofp);
3478						php_error_docref(NULL, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
3479						RETURN_FALSE;
3480					}
3481					efree(pgsql_lofp);
3482					php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3483					RETURN_FALSE;
3484				} else {
3485					pgsql_lofp->conn = pgsql;
3486					pgsql_lofp->lofd = pgsql_lofd;
3487					RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3488				}
3489			}
3490		} else {
3491			efree(pgsql_lofp);
3492			php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3493			RETURN_FALSE;
3494		}
3495	} else {
3496		pgsql_lofp->conn = pgsql;
3497		pgsql_lofp->lofd = pgsql_lofd;
3498		RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3499	}
3500}
3501/* }}} */
3502
3503/* {{{ proto bool pg_lo_close(resource large_object)
3504   Close a large object */
3505PHP_FUNCTION(pg_lo_close)
3506{
3507	zval *pgsql_lofp;
3508	pgLofp *pgsql;
3509
3510	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_lofp) == FAILURE) {
3511		return;
3512	}
3513
3514	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_lofp), "PostgreSQL large object", le_lofp)) == NULL) {
3515		RETURN_FALSE;
3516	}
3517
3518	if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
3519		php_error_docref(NULL, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
3520		RETVAL_FALSE;
3521	} else {
3522		RETVAL_TRUE;
3523	}
3524
3525	zend_list_close(Z_RES_P(pgsql_lofp));
3526	return;
3527}
3528/* }}} */
3529
3530#define PGSQL_LO_READ_BUF_SIZE  8192
3531
3532/* {{{ proto string pg_lo_read(resource large_object [, int len])
3533   Read a large object */
3534PHP_FUNCTION(pg_lo_read)
3535{
3536	zval *pgsql_id;
3537	zend_long len;
3538	size_t buf_len = PGSQL_LO_READ_BUF_SIZE;
3539	int nbytes, argc = ZEND_NUM_ARGS();
3540	zend_string *buf;
3541	pgLofp *pgsql;
3542
3543	if (zend_parse_parameters(argc, "r|l", &pgsql_id, &len) == FAILURE) {
3544		return;
3545	}
3546
3547	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3548		RETURN_FALSE;
3549	}
3550
3551	if (argc > 1) {
3552		buf_len = len < 0 ? 0 : len;
3553	}
3554
3555	buf = zend_string_alloc(buf_len, 0);
3556	if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(buf), ZSTR_LEN(buf)))<0) {
3557		zend_string_free(buf);
3558		RETURN_FALSE;
3559	}
3560
3561	ZSTR_LEN(buf) = nbytes;
3562	ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
3563	RETURN_NEW_STR(buf);
3564}
3565/* }}} */
3566
3567/* {{{ proto int pg_lo_write(resource large_object, string buf [, int len])
3568   Write a large object */
3569PHP_FUNCTION(pg_lo_write)
3570{
3571  	zval *pgsql_id;
3572  	char *str;
3573  	zend_long z_len;
3574	size_t str_len, nbytes;
3575	size_t len;
3576	pgLofp *pgsql;
3577	int argc = ZEND_NUM_ARGS();
3578
3579	if (zend_parse_parameters(argc, "rs|l", &pgsql_id, &str, &str_len, &z_len) == FAILURE) {
3580		return;
3581	}
3582
3583	if (argc > 2) {
3584		if (z_len > (zend_long)str_len) {
3585			php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %d. Tried to write %pd", str_len, z_len);
3586			RETURN_FALSE;
3587		}
3588		if (z_len < 0) {
3589			php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but %pd was specified", z_len);
3590			RETURN_FALSE;
3591		}
3592		len = z_len;
3593	}
3594	else {
3595		len = str_len;
3596	}
3597
3598	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3599		RETURN_FALSE;
3600	}
3601
3602	if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == -1) {
3603		RETURN_FALSE;
3604	}
3605
3606	RETURN_LONG(nbytes);
3607}
3608/* }}} */
3609
3610/* {{{ proto int pg_lo_read_all(resource large_object)
3611   Read a large object and send straight to browser */
3612PHP_FUNCTION(pg_lo_read_all)
3613{
3614  	zval *pgsql_id;
3615	int tbytes;
3616	volatile int nbytes;
3617	char buf[PGSQL_LO_READ_BUF_SIZE];
3618	pgLofp *pgsql;
3619
3620	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_id) == FAILURE) {
3621		return;
3622	}
3623
3624	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3625		RETURN_FALSE;
3626	}
3627
3628	tbytes = 0;
3629	while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
3630		PHPWRITE(buf, nbytes);
3631		tbytes += nbytes;
3632	}
3633	RETURN_LONG(tbytes);
3634}
3635/* }}} */
3636
3637/* {{{ proto int pg_lo_import([resource connection, ] string filename [, mixed oid])
3638   Import large object direct from filesystem */
3639PHP_FUNCTION(pg_lo_import)
3640{
3641	zval *pgsql_link = NULL, *oid = NULL;
3642	char *file_in;
3643	size_t name_len;
3644	int argc = ZEND_NUM_ARGS();
3645	PGconn *pgsql;
3646	Oid returned_oid;
3647	zend_resource *link;
3648
3649	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3650								 "rp|z", &pgsql_link, &file_in, &name_len, &oid) == SUCCESS) {
3651		link = Z_RES_P(pgsql_link);
3652	}
3653	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3654									  "p|z", &file_in, &name_len, &oid) == SUCCESS) {
3655		link = FETCH_DEFAULT_LINK();
3656		CHECK_DEFAULT_LINK(link);
3657	}
3658	/* old calling convention, deprecated since PHP 4.2 */
3659	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3660									  "pr", &file_in, &name_len, &pgsql_link ) == SUCCESS) {
3661		php_error_docref(NULL, E_NOTICE, "Old API is used");
3662		link = Z_RES_P(pgsql_link);
3663	}
3664	else {
3665		WRONG_PARAM_COUNT;
3666	}
3667
3668	if (php_check_open_basedir(file_in)) {
3669		RETURN_FALSE;
3670	}
3671
3672	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3673		RETURN_FALSE;
3674	}
3675
3676	if (oid) {
3677#ifndef HAVE_PG_LO_IMPORT_WITH_OID
3678		php_error_docref(NULL, E_NOTICE, "OID value passing not supported");
3679#else
3680		Oid wanted_oid;
3681		switch (Z_TYPE_P(oid)) {
3682		case IS_STRING:
3683			{
3684				char *end_ptr;
3685				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3686				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3687				/* wrong integer format */
3688				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3689				RETURN_FALSE;
3690				}
3691			}
3692			break;
3693		case IS_LONG:
3694			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3695				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3696				RETURN_FALSE;
3697			}
3698			wanted_oid = (Oid)Z_LVAL_P(oid);
3699			break;
3700		default:
3701			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3702			RETURN_FALSE;
3703        }
3704
3705       returned_oid = lo_import_with_oid(pgsql, file_in, wanted_oid);
3706
3707	   if (returned_oid == InvalidOid) {
3708		   RETURN_FALSE;
3709	   }
3710
3711	   PGSQL_RETURN_OID(returned_oid);
3712#endif
3713	}
3714
3715	returned_oid = lo_import(pgsql, file_in);
3716
3717	if (returned_oid == InvalidOid) {
3718		RETURN_FALSE;
3719	}
3720	PGSQL_RETURN_OID(returned_oid);
3721}
3722/* }}} */
3723
3724/* {{{ proto bool pg_lo_export([resource connection, ] int objoid, string filename)
3725   Export large object direct to filesystem */
3726PHP_FUNCTION(pg_lo_export)
3727{
3728	zval *pgsql_link = NULL;
3729	char *file_out, *oid_string, *end_ptr;
3730	size_t oid_strlen;
3731	size_t name_len;
3732	zend_long oid_long;
3733	Oid oid;
3734	PGconn *pgsql;
3735	int argc = ZEND_NUM_ARGS();
3736	zend_resource *link;
3737
3738	/* allow string to handle large OID value correctly */
3739	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3740								 "rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
3741		if (oid_long <= InvalidOid) {
3742			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3743			RETURN_FALSE;
3744		}
3745		oid = (Oid)oid_long;
3746		link = Z_RES_P(pgsql_link);
3747	}
3748	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3749								 "rss", &pgsql_link, &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3750		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3751		if ((oid_string+oid_strlen) != end_ptr) {
3752			/* wrong integer format */
3753			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3754			RETURN_FALSE;
3755		}
3756		link = Z_RES_P(pgsql_link);
3757	}
3758	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3759									  "lp",  &oid_long, &file_out, &name_len) == SUCCESS) {
3760		if (oid_long <= InvalidOid) {
3761			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3762			RETURN_FALSE;
3763		}
3764		oid = (Oid)oid_long;
3765		link = FETCH_DEFAULT_LINK();
3766		CHECK_DEFAULT_LINK(link);
3767	}
3768	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3769								 "sp", &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3770		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3771		if ((oid_string+oid_strlen) != end_ptr) {
3772			/* wrong integer format */
3773			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3774			RETURN_FALSE;
3775		}
3776		link = FETCH_DEFAULT_LINK();
3777		CHECK_DEFAULT_LINK(link);
3778	}
3779	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3780								 "spr", &oid_string, &oid_strlen, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3781		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3782		if ((oid_string+oid_strlen) != end_ptr) {
3783			/* wrong integer format */
3784			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3785			RETURN_FALSE;
3786		}
3787		link = Z_RES_P(pgsql_link);
3788	}
3789	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3790									  "lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3791		php_error_docref(NULL, E_NOTICE, "Old API is used");
3792		if (oid_long <= InvalidOid) {
3793			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3794			RETURN_FALSE;
3795		}
3796		oid = (Oid)oid_long;
3797		link = Z_RES_P(pgsql_link);
3798	}
3799	else {
3800		php_error_docref(NULL, E_WARNING, "Requires 2 or 3 arguments");
3801		RETURN_FALSE;
3802	}
3803
3804	if (php_check_open_basedir(file_out)) {
3805		RETURN_FALSE;
3806	}
3807
3808	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3809		RETURN_FALSE;
3810	}
3811
3812	if (lo_export(pgsql, oid, file_out) == -1) {
3813		RETURN_FALSE;
3814	}
3815	RETURN_TRUE;
3816}
3817/* }}} */
3818
3819/* {{{ proto bool pg_lo_seek(resource large_object, int offset [, int whence])
3820   Seeks position of large object */
3821PHP_FUNCTION(pg_lo_seek)
3822{
3823	zval *pgsql_id = NULL;
3824	zend_long result, offset = 0, whence = SEEK_CUR;
3825	pgLofp *pgsql;
3826	int argc = ZEND_NUM_ARGS();
3827
3828	if (zend_parse_parameters(argc, "rl|l", &pgsql_id, &offset, &whence) == FAILURE) {
3829		return;
3830	}
3831	if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
3832		php_error_docref(NULL, E_WARNING, "Invalid whence parameter");
3833		return;
3834	}
3835
3836	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3837		RETURN_FALSE;
3838	}
3839
3840#if HAVE_PG_LO64
3841	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3842		result = lo_lseek64((PGconn *)pgsql->conn, pgsql->lofd, offset, (int)whence);
3843	} else {
3844		result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, (int)offset, (int)whence);
3845	}
3846#else
3847	result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence);
3848#endif
3849	if (result > -1) {
3850		RETURN_TRUE;
3851	} else {
3852		RETURN_FALSE;
3853	}
3854}
3855/* }}} */
3856
3857/* {{{ proto int pg_lo_tell(resource large_object)
3858   Returns current position of large object */
3859PHP_FUNCTION(pg_lo_tell)
3860{
3861	zval *pgsql_id = NULL;
3862	zend_long offset = 0;
3863	pgLofp *pgsql;
3864	int argc = ZEND_NUM_ARGS();
3865
3866	if (zend_parse_parameters(argc, "r", &pgsql_id) == FAILURE) {
3867		return;
3868	}
3869
3870	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3871		RETURN_FALSE;
3872	}
3873
3874#if HAVE_PG_LO64
3875	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3876		offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
3877	} else {
3878		offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3879	}
3880#else
3881	offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3882#endif
3883	RETURN_LONG(offset);
3884}
3885/* }}} */
3886
3887#if HAVE_PG_LO_TRUNCATE
3888/* {{{ proto bool pg_lo_truncate(resource large_object, int size)
3889   Truncate large object to size */
3890PHP_FUNCTION(pg_lo_truncate)
3891{
3892	zval *pgsql_id = NULL;
3893	size_t size;
3894	pgLofp *pgsql;
3895	int argc = ZEND_NUM_ARGS();
3896	int result;
3897
3898	if (zend_parse_parameters(argc, "rl", &pgsql_id, &size) == FAILURE) {
3899		return;
3900	}
3901
3902	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3903		RETURN_FALSE;
3904	}
3905
3906#if HAVE_PG_LO64
3907	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3908		result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
3909	} else {
3910		result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3911	}
3912#else
3913	result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3914#endif
3915	if (!result) {
3916		RETURN_TRUE;
3917	} else {
3918		RETURN_FALSE;
3919	}
3920}
3921/* }}} */
3922#endif
3923
3924#if HAVE_PQSETERRORVERBOSITY
3925/* {{{ proto int pg_set_error_verbosity([resource connection,] int verbosity)
3926   Set error verbosity */
3927PHP_FUNCTION(pg_set_error_verbosity)
3928{
3929	zval *pgsql_link = NULL;
3930	zend_long verbosity;
3931	int argc = ZEND_NUM_ARGS();
3932	PGconn *pgsql;
3933	zend_resource *link;
3934
3935	if (argc == 1) {
3936		if (zend_parse_parameters(argc, "l", &verbosity) == FAILURE) {
3937			return;
3938		}
3939		link = FETCH_DEFAULT_LINK();
3940		CHECK_DEFAULT_LINK(link);
3941	} else {
3942		if (zend_parse_parameters(argc, "rl", &pgsql_link, &verbosity) == FAILURE) {
3943			return;
3944		}
3945		link = Z_RES_P(pgsql_link);
3946	}
3947
3948	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3949		RETURN_FALSE;
3950	}
3951
3952	if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE)) {
3953		RETURN_LONG(PQsetErrorVerbosity(pgsql, verbosity));
3954	} else {
3955		RETURN_FALSE;
3956	}
3957}
3958/* }}} */
3959#endif
3960
3961#ifdef HAVE_PQCLIENTENCODING
3962/* {{{ proto int pg_set_client_encoding([resource connection,] string encoding)
3963   Set client encoding */
3964PHP_FUNCTION(pg_set_client_encoding)
3965{
3966	char *encoding;
3967	size_t encoding_len;
3968	zval *pgsql_link = NULL;
3969	int argc = ZEND_NUM_ARGS();
3970	PGconn *pgsql;
3971	zend_resource *link;
3972
3973	if (argc == 1) {
3974		if (zend_parse_parameters(argc, "s", &encoding, &encoding_len) == FAILURE) {
3975			return;
3976		}
3977		link = FETCH_DEFAULT_LINK();
3978		CHECK_DEFAULT_LINK(link);
3979	} else {
3980		if (zend_parse_parameters(argc, "rs", &pgsql_link, &encoding, &encoding_len) == FAILURE) {
3981			return;
3982		}
3983		link = Z_RES_P(pgsql_link);
3984	}
3985
3986	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3987		RETURN_FALSE;
3988	}
3989
3990	RETURN_LONG(PQsetClientEncoding(pgsql, encoding));
3991}
3992/* }}} */
3993
3994/* {{{ proto string pg_client_encoding([resource connection])
3995   Get the current client encoding */
3996PHP_FUNCTION(pg_client_encoding)
3997{
3998	zval *pgsql_link = NULL;
3999	int argc = ZEND_NUM_ARGS();
4000	PGconn *pgsql;
4001	zend_resource *link;
4002
4003	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
4004		return;
4005	}
4006
4007	if (argc == 0) {
4008		link = FETCH_DEFAULT_LINK();
4009		CHECK_DEFAULT_LINK(link);
4010	} else {
4011		link = Z_RES_P(pgsql_link);
4012	}
4013
4014	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4015		RETURN_FALSE;
4016	}
4017
4018	/* Just do the same as found in PostgreSQL sources... */
4019
4020	RETURN_STRING((char *) pg_encoding_to_char(PQclientEncoding(pgsql)));
4021}
4022/* }}} */
4023#endif
4024
4025#if !HAVE_PQGETCOPYDATA
4026#define	COPYBUFSIZ	8192
4027#endif
4028
4029/* {{{ proto bool pg_end_copy([resource connection])
4030   Sync with backend. Completes the Copy command */
4031PHP_FUNCTION(pg_end_copy)
4032{
4033	zval *pgsql_link = NULL;
4034	int argc = ZEND_NUM_ARGS();
4035	PGconn *pgsql;
4036	int result = 0;
4037	zend_resource *link;
4038
4039	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
4040		return;
4041	}
4042
4043	if (argc == 0) {
4044		link = FETCH_DEFAULT_LINK();
4045		CHECK_DEFAULT_LINK(link);
4046	} else {
4047		link = Z_RES_P(pgsql_link);
4048	}
4049
4050	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4051		RETURN_FALSE;
4052	}
4053
4054	result = PQendcopy(pgsql);
4055
4056	if (result!=0) {
4057		PHP_PQ_ERROR("Query failed: %s", pgsql);
4058		RETURN_FALSE;
4059	}
4060	RETURN_TRUE;
4061}
4062/* }}} */
4063
4064/* {{{ proto bool pg_put_line([resource connection,] string query)
4065   Send null-terminated string to backend server*/
4066PHP_FUNCTION(pg_put_line)
4067{
4068	char *query;
4069	zval *pgsql_link = NULL;
4070	size_t query_len;
4071	PGconn *pgsql;
4072	zend_resource *link;
4073	int result = 0, argc = ZEND_NUM_ARGS();
4074
4075	if (argc == 1) {
4076		if (zend_parse_parameters(argc, "s", &query, &query_len) == FAILURE) {
4077			return;
4078		}
4079		link = FETCH_DEFAULT_LINK();
4080		CHECK_DEFAULT_LINK(link);
4081	} else {
4082		if (zend_parse_parameters(argc, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
4083			return;
4084		}
4085		link = Z_RES_P(pgsql_link);
4086	}
4087
4088	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4089		RETURN_FALSE;
4090	}
4091
4092	result = PQputline(pgsql, query);
4093	if (result==EOF) {
4094		PHP_PQ_ERROR("Query failed: %s", pgsql);
4095		RETURN_FALSE;
4096	}
4097	RETURN_TRUE;
4098}
4099/* }}} */
4100
4101/* {{{ proto array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])
4102   Copy table to array */
4103PHP_FUNCTION(pg_copy_to)
4104{
4105	zval *pgsql_link;
4106	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4107	size_t table_name_len, pg_delim_len, pg_null_as_len, free_pg_null = 0;
4108	char *query;
4109	PGconn *pgsql;
4110	PGresult *pgsql_result;
4111	ExecStatusType status;
4112	int copydone = 0;
4113#if !HAVE_PQGETCOPYDATA
4114	char copybuf[COPYBUFSIZ];
4115#endif
4116	char *csv = (char *)NULL;
4117	int ret;
4118	int argc = ZEND_NUM_ARGS();
4119
4120	if (zend_parse_parameters(argc, "rs|ss",
4121							  &pgsql_link, &table_name, &table_name_len,
4122							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4123		return;
4124	}
4125	if (!pg_delim) {
4126		pg_delim = "\t";
4127	}
4128
4129	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4130		RETURN_FALSE;
4131	}
4132
4133	if (!pg_null_as) {
4134		pg_null_as = estrdup("\\\\N");
4135		free_pg_null = 1;
4136	}
4137
4138	spprintf(&query, 0, "COPY %s TO STDOUT DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4139
4140	while ((pgsql_result = PQgetResult(pgsql))) {
4141		PQclear(pgsql_result);
4142	}
4143	pgsql_result = PQexec(pgsql, query);
4144	if (free_pg_null) {
4145		efree(pg_null_as);
4146	}
4147	efree(query);
4148
4149	if (pgsql_result) {
4150		status = PQresultStatus(pgsql_result);
4151	} else {
4152		status = (ExecStatusType) PQstatus(pgsql);
4153	}
4154
4155	switch (status) {
4156		case PGRES_COPY_OUT:
4157			if (pgsql_result) {
4158				PQclear(pgsql_result);
4159				array_init(return_value);
4160#if HAVE_PQGETCOPYDATA
4161				while (!copydone)
4162				{
4163					ret = PQgetCopyData(pgsql, &csv, 0);
4164					switch (ret) {
4165						case -1:
4166							copydone = 1;
4167							break;
4168						case 0:
4169						case -2:
4170							PHP_PQ_ERROR("getline failed: %s", pgsql);
4171							RETURN_FALSE;
4172							break;
4173						default:
4174							add_next_index_string(return_value, csv);
4175							PQfreemem(csv);
4176							break;
4177					}
4178				}
4179#else
4180				while (!copydone)
4181				{
4182					if ((ret = PQgetline(pgsql, copybuf, COPYBUFSIZ))) {
4183						PHP_PQ_ERROR("getline failed: %s", pgsql);
4184						RETURN_FALSE;
4185					}
4186
4187					if (copybuf[0] == '\\' &&
4188						copybuf[1] == '.' &&
4189						copybuf[2] == '\0')
4190					{
4191						copydone = 1;
4192					}
4193					else
4194					{
4195						if (csv == (char *)NULL) {
4196							csv = estrdup(copybuf);
4197						} else {
4198							csv = (char *)erealloc(csv, strlen(csv) + sizeof(char)*(COPYBUFSIZ+1));
4199							strcat(csv, copybuf);
4200						}
4201
4202						switch (ret)
4203						{
4204							case EOF:
4205								copydone = 1;
4206							case 0:
4207								add_next_index_string(return_value, csv);
4208								efree(csv);
4209								csv = (char *)NULL;
4210								break;
4211							case 1:
4212								break;
4213						}
4214					}
4215				}
4216				if (PQendcopy(pgsql)) {
4217					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4218					RETURN_FALSE;
4219				}
4220#endif
4221				while ((pgsql_result = PQgetResult(pgsql))) {
4222					PQclear(pgsql_result);
4223				}
4224			} else {
4225				PQclear(pgsql_result);
4226				RETURN_FALSE;
4227			}
4228			break;
4229		default:
4230			PQclear(pgsql_result);
4231			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4232			RETURN_FALSE;
4233			break;
4234	}
4235}
4236/* }}} */
4237
4238/* {{{ proto bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])
4239   Copy table from array */
4240PHP_FUNCTION(pg_copy_from)
4241{
4242	zval *pgsql_link = NULL, *pg_rows;
4243	zval *value;
4244	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4245	size_t  table_name_len, pg_delim_len, pg_null_as_len;
4246	int  pg_null_as_free = 0;
4247	char *query;
4248	PGconn *pgsql;
4249	PGresult *pgsql_result;
4250	ExecStatusType status;
4251	int argc = ZEND_NUM_ARGS();
4252
4253	if (zend_parse_parameters(argc, "rsa|ss",
4254							  &pgsql_link, &table_name, &table_name_len, &pg_rows,
4255							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4256		return;
4257	}
4258	if (!pg_delim) {
4259		pg_delim = "\t";
4260	}
4261	if (!pg_null_as) {
4262		pg_null_as = estrdup("\\\\N");
4263		pg_null_as_free = 1;
4264	}
4265
4266	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4267		RETURN_FALSE;
4268	}
4269
4270	spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4271	while ((pgsql_result = PQgetResult(pgsql))) {
4272		PQclear(pgsql_result);
4273	}
4274	pgsql_result = PQexec(pgsql, query);
4275
4276	if (pg_null_as_free) {
4277		efree(pg_null_as);
4278	}
4279	efree(query);
4280
4281	if (pgsql_result) {
4282		status = PQresultStatus(pgsql_result);
4283	} else {
4284		status = (ExecStatusType) PQstatus(pgsql);
4285	}
4286
4287	switch (status) {
4288		case PGRES_COPY_IN:
4289			if (pgsql_result) {
4290				int command_failed = 0;
4291				PQclear(pgsql_result);
4292#if HAVE_PQPUTCOPYDATA
4293				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4294					zval tmp;
4295					ZVAL_COPY(&tmp, value);
4296					convert_to_string_ex(&tmp);
4297					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4298					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4299					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4300						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4301					}
4302					if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) {
4303						efree(query);
4304						zval_dtor(&tmp);
4305						PHP_PQ_ERROR("copy failed: %s", pgsql);
4306						RETURN_FALSE;
4307					}
4308					efree(query);
4309					zval_dtor(&tmp);
4310				} ZEND_HASH_FOREACH_END();
4311
4312				if (PQputCopyEnd(pgsql, NULL) != 1) {
4313					PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
4314					RETURN_FALSE;
4315				}
4316#else
4317				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4318					zval tmp;
4319					ZVAL_COPY(&tmp, value);
4320					convert_to_string_ex(&tmp);
4321					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4322					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4323					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4324						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4325					}
4326					if (PQputline(pgsql, query)==EOF) {
4327						efree(query);
4328						zval_dtor(&tmp);
4329						PHP_PQ_ERROR("copy failed: %s", pgsql);
4330						RETURN_FALSE;
4331					}
4332					efree(query);
4333					zval_dtor(&tmp);
4334				} ZEND_HASH_FOREACH_END();
4335
4336				if (PQputline(pgsql, "\\.\n") == EOF) {
4337					PHP_PQ_ERROR("putline failed: %s", pgsql);
4338					RETURN_FALSE;
4339				}
4340				if (PQendcopy(pgsql)) {
4341					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4342					RETURN_FALSE;
4343				}
4344#endif
4345				while ((pgsql_result = PQgetResult(pgsql))) {
4346					if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
4347						PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4348						command_failed = 1;
4349					}
4350					PQclear(pgsql_result);
4351				}
4352				if (command_failed) {
4353					RETURN_FALSE;
4354				}
4355			} else {
4356				PQclear(pgsql_result);
4357				RETURN_FALSE;
4358			}
4359			RETURN_TRUE;
4360			break;
4361		default:
4362			PQclear(pgsql_result);
4363			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4364			RETURN_FALSE;
4365			break;
4366	}
4367}
4368/* }}} */
4369
4370#ifdef HAVE_PQESCAPE
4371/* {{{ proto string pg_escape_string([resource connection,] string data)
4372   Escape string for text/char type */
4373PHP_FUNCTION(pg_escape_string)
4374{
4375	zend_string *from = NULL, *to = NULL;
4376	zval *pgsql_link;
4377	zend_resource *link;
4378#ifdef HAVE_PQESCAPE_CONN
4379	PGconn *pgsql;
4380#endif
4381
4382	switch (ZEND_NUM_ARGS()) {
4383		case 1:
4384			if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
4385				return;
4386			}
4387			link = FETCH_DEFAULT_LINK();
4388			break;
4389		default:
4390			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &pgsql_link, &from) == FAILURE) {
4391				return;
4392			}
4393			link = Z_RES_P(pgsql_link);
4394			break;
4395	}
4396
4397	to = zend_string_alloc(ZSTR_LEN(from) * 2, 0);
4398#ifdef HAVE_PQESCAPE_CONN
4399	if (link) {
4400		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4401			RETURN_FALSE;
4402		}
4403		ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL);
4404	} else
4405#endif
4406	{
4407		ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from));
4408	}
4409
4410	to = zend_string_truncate(to, ZSTR_LEN(to), 0);
4411	RETURN_NEW_STR(to);
4412}
4413/* }}} */
4414
4415/* {{{ proto string pg_escape_bytea([resource connection,] string data)
4416   Escape binary for bytea type  */
4417PHP_FUNCTION(pg_escape_bytea)
4418{
4419	char *from = NULL, *to = NULL;
4420	size_t to_len;
4421	size_t from_len;
4422#ifdef HAVE_PQESCAPE_BYTEA_CONN
4423	PGconn *pgsql;
4424#endif
4425	zval *pgsql_link;
4426	zend_resource *link;
4427
4428	switch (ZEND_NUM_ARGS()) {
4429		case 1:
4430			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4431				return;
4432			}
4433			link = FETCH_DEFAULT_LINK();
4434			break;
4435		default:
4436			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4437				return;
4438			}
4439			link = Z_RES_P(pgsql_link);
4440			break;
4441	}
4442
4443#ifdef HAVE_PQESCAPE_BYTEA_CONN
4444	if (link) {
4445		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4446			RETURN_FALSE;
4447		}
4448		to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)from, (size_t)from_len, &to_len);
4449	} else
4450#endif
4451		to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
4452
4453	RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */
4454}
4455/* }}} */
4456
4457#if !HAVE_PQUNESCAPEBYTEA
4458/* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
4459   Renamed to php_pgsql_unescape_bytea() */
4460/*
4461 *		PQunescapeBytea - converts the null terminated string representation
4462 *		of a bytea, strtext, into binary, filling a buffer. It returns a
4463 *		pointer to the buffer which is NULL on error, and the size of the
4464 *		buffer in retbuflen. The pointer may subsequently be used as an
4465 *		argument to the function free(3). It is the reverse of PQescapeBytea.
4466 *
4467 *		The following transformations are reversed:
4468 *		'\0' == ASCII  0 == \000
4469 *		'\'' == ASCII 39 == \'
4470 *		'\\' == ASCII 92 == \\
4471 *
4472 *		States:
4473 *		0	normal		0->1->2->3->4
4474 *		1	\			   1->5
4475 *		2	\0			   1->6
4476 *		3	\00
4477 *		4	\000
4478 *		5	\'
4479 *		6	\\
4480 */
4481static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) /* {{{ */
4482{
4483	size_t     buflen;
4484	unsigned char *buffer,
4485			   *sp,
4486			   *bp;
4487	unsigned int state = 0;
4488
4489	if (strtext == NULL)
4490		return NULL;
4491	buflen = strlen(strtext);	/* will shrink, also we discover if
4492								 * strtext */
4493	buffer = (unsigned char *) emalloc(buflen);	/* isn't NULL terminated */
4494	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
4495	{
4496		switch (state)
4497		{
4498			case 0:
4499				if (*sp == '\\')
4500					state = 1;
4501				*bp = *sp;
4502				break;
4503			case 1:
4504				if (*sp == '\'')	/* state=5 */
4505				{				/* replace \' with 39 */
4506					bp--;
4507					*bp = '\'';
4508					buflen--;
4509					state = 0;
4510				}
4511				else if (*sp == '\\')	/* state=6 */
4512				{				/* replace \\ with 92 */
4513					bp--;
4514					*bp = '\\';
4515					buflen--;
4516					state = 0;
4517				}
4518				else
4519				{
4520					if (isdigit(*sp))
4521						state = 2;
4522					else
4523						state = 0;
4524					*bp = *sp;
4525				}
4526				break;
4527			case 2:
4528				if (isdigit(*sp))
4529					state = 3;
4530				else
4531					state = 0;
4532				*bp = *sp;
4533				break;
4534			case 3:
4535				if (isdigit(*sp))		/* state=4 */
4536				{
4537					unsigned char *start, *end, buf[4]; /* 000 + '\0' */
4538
4539					bp -= 3;
4540					memcpy(buf, sp-2, 3);
4541					buf[3] = '\0';
4542					start = buf;
4543					*bp = (unsigned char)strtoul(start, (char **)&end, 8);
4544					buflen -= 3;
4545					state = 0;
4546				}
4547				else
4548				{
4549					*bp = *sp;
4550					state = 0;
4551				}
4552				break;
4553		}
4554	}
4555	buffer = erealloc(buffer, buflen+1);
4556	buffer[buflen] = '\0';
4557
4558	*retbuflen = buflen;
4559	return buffer;
4560}
4561/* }}} */
4562#endif
4563
4564/* {{{ proto string pg_unescape_bytea(string data)
4565   Unescape binary for bytea type  */
4566PHP_FUNCTION(pg_unescape_bytea)
4567{
4568	char *from = NULL, *to = NULL, *tmp = NULL;
4569	size_t to_len;
4570	size_t from_len;
4571	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
4572							  &from, &from_len) == FAILURE) {
4573		return;
4574	}
4575
4576#if HAVE_PQUNESCAPEBYTEA
4577	tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
4578	to = estrndup(tmp, to_len);
4579	PQfreemem(tmp);
4580#else
4581	to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
4582#endif
4583	if (!to) {
4584		php_error_docref(NULL, E_WARNING,"Invalid parameter");
4585		RETURN_FALSE;
4586	}
4587	RETVAL_STRINGL(to, to_len);
4588	efree(to);
4589}
4590/* }}} */
4591#endif
4592
4593#ifdef HAVE_PQESCAPE
4594static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) /* {{{ */ {
4595	char *from = NULL;
4596	zval *pgsql_link = NULL;
4597	PGconn *pgsql;
4598	size_t from_len;
4599	char *tmp;
4600	zend_resource *link;
4601
4602	switch (ZEND_NUM_ARGS()) {
4603		case 1:
4604			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4605				return;
4606			}
4607			link = FETCH_DEFAULT_LINK();
4608			CHECK_DEFAULT_LINK(link);
4609			break;
4610
4611		default:
4612			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4613				return;
4614			}
4615			link = Z_RES_P(pgsql_link);
4616			break;
4617	}
4618
4619	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4620		RETURN_FALSE;
4621	}
4622
4623	if (pgsql == NULL) {
4624		php_error_docref(NULL, E_WARNING,"Cannot get pgsql link");
4625		RETURN_FALSE;
4626	}
4627
4628	if (escape_literal) {
4629		tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
4630	} else {
4631		tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
4632	}
4633	if (!tmp) {
4634		php_error_docref(NULL, E_WARNING,"Failed to escape");
4635		RETURN_FALSE;
4636	}
4637
4638	RETVAL_STRING(tmp);
4639	PGSQLfree(tmp);
4640}
4641/* }}} */
4642
4643/* {{{ proto string pg_escape_literal([resource connection,] string data)
4644   Escape parameter as string literal (i.e. parameter)	*/
4645PHP_FUNCTION(pg_escape_literal)
4646{
4647	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4648}
4649/* }}} */
4650
4651/* {{{ proto string pg_escape_identifier([resource connection,] string data)
4652   Escape identifier (i.e. table name, field name)	*/
4653PHP_FUNCTION(pg_escape_identifier)
4654{
4655	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4656}
4657/* }}} */
4658#endif
4659
4660/* {{{ proto string pg_result_error(resource result)
4661   Get error message associated with result */
4662PHP_FUNCTION(pg_result_error)
4663{
4664	zval *result;
4665	PGresult *pgsql_result;
4666	pgsql_result_handle *pg_result;
4667	char *err = NULL;
4668
4669	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4670								 &result) == FAILURE) {
4671		RETURN_FALSE;
4672	}
4673
4674	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4675		RETURN_FALSE;
4676	}
4677
4678	pgsql_result = pg_result->result;
4679	if (!pgsql_result) {
4680		RETURN_FALSE;
4681	}
4682	err = (char *)PQresultErrorMessage(pgsql_result);
4683	RETURN_STRING(err);
4684}
4685/* }}} */
4686
4687#if HAVE_PQRESULTERRORFIELD
4688/* {{{ proto string pg_result_error_field(resource result, int fieldcode)
4689   Get error message field associated with result */
4690PHP_FUNCTION(pg_result_error_field)
4691{
4692	zval *result;
4693	zend_long fieldcode;
4694	PGresult *pgsql_result;
4695	pgsql_result_handle *pg_result;
4696	char *field = NULL;
4697
4698	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rl",
4699								 &result, &fieldcode) == FAILURE) {
4700		RETURN_FALSE;
4701	}
4702
4703	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4704		RETURN_FALSE;
4705	}
4706
4707	pgsql_result = pg_result->result;
4708	if (!pgsql_result) {
4709		RETURN_FALSE;
4710	}
4711	if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
4712				|PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
4713#if PG_DIAG_INTERNAL_POSITION
4714				|PG_DIAG_INTERNAL_POSITION
4715#endif
4716#if PG_DIAG_INTERNAL_QUERY
4717				|PG_DIAG_INTERNAL_QUERY
4718#endif
4719				|PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
4720				|PG_DIAG_SOURCE_FUNCTION)) {
4721		field = (char *)PQresultErrorField(pgsql_result, (int)fieldcode);
4722		if (field == NULL) {
4723			RETURN_NULL();
4724		} else {
4725			RETURN_STRING(field);
4726		}
4727	} else {
4728		RETURN_FALSE;
4729	}
4730}
4731/* }}} */
4732#endif
4733
4734/* {{{ proto int pg_connection_status(resource connection)
4735   Get connection status */
4736PHP_FUNCTION(pg_connection_status)
4737{
4738	zval *pgsql_link = NULL;
4739	PGconn *pgsql;
4740
4741	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4742								 &pgsql_link) == FAILURE) {
4743		RETURN_FALSE;
4744	}
4745
4746	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4747		RETURN_FALSE;
4748	}
4749
4750	RETURN_LONG(PQstatus(pgsql));
4751}
4752
4753/* }}} */
4754
4755#if HAVE_PGTRANSACTIONSTATUS
4756/* {{{ proto int pg_transaction_status(resource connection)
4757   Get transaction status */
4758PHP_FUNCTION(pg_transaction_status)
4759{
4760	zval *pgsql_link = NULL;
4761	PGconn *pgsql;
4762
4763	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4764								 &pgsql_link) == FAILURE) {
4765		RETURN_FALSE;
4766	}
4767
4768	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4769		RETURN_FALSE;
4770	}
4771
4772	RETURN_LONG(PQtransactionStatus(pgsql));
4773}
4774#endif
4775
4776/* }}} */
4777
4778/* {{{ proto bool pg_connection_reset(resource connection)
4779   Reset connection (reconnect) */
4780PHP_FUNCTION(pg_connection_reset)
4781{
4782	zval *pgsql_link;
4783	PGconn *pgsql;
4784
4785	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4786								 &pgsql_link) == FAILURE) {
4787		RETURN_FALSE;
4788	}
4789
4790	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4791		RETURN_FALSE;
4792	}
4793
4794	PQreset(pgsql);
4795	if (PQstatus(pgsql) == CONNECTION_BAD) {
4796		RETURN_FALSE;
4797	}
4798	RETURN_TRUE;
4799}
4800/* }}} */
4801
4802#define PHP_PG_ASYNC_IS_BUSY		1
4803#define PHP_PG_ASYNC_REQUEST_CANCEL 2
4804
4805/* {{{ php_pgsql_flush_query
4806 */
4807static int php_pgsql_flush_query(PGconn *pgsql)
4808{
4809	PGresult *res;
4810	int leftover = 0;
4811
4812	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4813		php_error_docref(NULL, E_NOTICE,"Cannot set connection to nonblocking mode");
4814		return -1;
4815	}
4816	while ((res = PQgetResult(pgsql))) {
4817		PQclear(res);
4818		leftover++;
4819	}
4820	PQ_SETNONBLOCKING(pgsql, 0);
4821	return leftover;
4822}
4823/* }}} */
4824
4825/* {{{ php_pgsql_do_async
4826 */
4827static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
4828{
4829	zval *pgsql_link;
4830	PGconn *pgsql;
4831	PGresult *pgsql_result;
4832
4833	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4834								 &pgsql_link) == FAILURE) {
4835		RETURN_FALSE;
4836	}
4837
4838	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4839		RETURN_FALSE;
4840	}
4841
4842	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4843		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4844		RETURN_FALSE;
4845	}
4846	switch(entry_type) {
4847		case PHP_PG_ASYNC_IS_BUSY:
4848			PQconsumeInput(pgsql);
4849			RETVAL_LONG(PQisBusy(pgsql));
4850			break;
4851		case PHP_PG_ASYNC_REQUEST_CANCEL:
4852			RETVAL_LONG(PQrequestCancel(pgsql));
4853			while ((pgsql_result = PQgetResult(pgsql))) {
4854				PQclear(pgsql_result);
4855			}
4856			break;
4857		default:
4858			php_error_docref(NULL, E_ERROR, "PostgreSQL module error, please report this error");
4859			break;
4860	}
4861	if (PQ_SETNONBLOCKING(pgsql, 0)) {
4862		php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4863	}
4864	convert_to_boolean_ex(return_value);
4865}
4866/* }}} */
4867
4868/* {{{ proto bool pg_cancel_query(resource connection)
4869   Cancel request */
4870PHP_FUNCTION(pg_cancel_query)
4871{
4872	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
4873}
4874/* }}} */
4875
4876/* {{{ proto bool pg_connection_busy(resource connection)
4877   Get connection is busy or not */
4878PHP_FUNCTION(pg_connection_busy)
4879{
4880	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
4881}
4882/* }}} */
4883
4884static int _php_pgsql_link_has_results(PGconn *pgsql) /* {{{ */
4885{
4886	PGresult *result;
4887	while ((result = PQgetResult(pgsql))) {
4888		PQclear(result);
4889		return 1;
4890	}
4891	return 0;
4892}
4893/* }}} */
4894
4895/* {{{ proto bool pg_send_query(resource connection, string query)
4896   Send asynchronous query */
4897PHP_FUNCTION(pg_send_query)
4898{
4899	zval *pgsql_link;
4900	char *query;
4901	size_t len;
4902	PGconn *pgsql;
4903	int is_non_blocking;
4904	int ret;
4905
4906	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &len) == FAILURE) {
4907		return;
4908	}
4909
4910	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4911		RETURN_FALSE;
4912	}
4913
4914	is_non_blocking = PQisnonblocking(pgsql);
4915
4916	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4917		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4918		RETURN_FALSE;
4919	}
4920
4921	if (_php_pgsql_link_has_results(pgsql)) {
4922		php_error_docref(NULL, E_NOTICE,
4923			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4924	}
4925
4926	if (is_non_blocking) {
4927		if (!PQsendQuery(pgsql, query)) {
4928			RETURN_FALSE;
4929		}
4930		ret = PQflush(pgsql);
4931	} else {
4932		if (!PQsendQuery(pgsql, query)) {
4933			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4934				PQreset(pgsql);
4935			}
4936			if (!PQsendQuery(pgsql, query)) {
4937				RETURN_FALSE;
4938			}
4939		}
4940
4941		/* Wait to finish sending buffer */
4942		while ((ret = PQflush(pgsql))) {
4943			if (ret == -1) {
4944				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
4945				break;
4946			}
4947			usleep(10000);
4948		}
4949
4950		if (PQ_SETNONBLOCKING(pgsql, 0)) {
4951			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4952		}
4953	}
4954
4955	if (ret == 0) {
4956		RETURN_TRUE;
4957	} else if (ret == -1) {
4958		RETURN_FALSE;
4959	} else {
4960		RETURN_LONG(0);
4961	}
4962}
4963/* }}} */
4964
4965#if HAVE_PQSENDQUERYPARAMS
4966/* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
4967   Send asynchronous parameterized query */
4968PHP_FUNCTION(pg_send_query_params)
4969{
4970	zval *pgsql_link, *pv_param_arr, *tmp;
4971	int num_params = 0;
4972	char **params = NULL;
4973	char *query;
4974	size_t query_len;
4975	PGconn *pgsql;
4976	int is_non_blocking;
4977	int ret;
4978
4979	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
4980		return;
4981	}
4982
4983	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4984		RETURN_FALSE;
4985	}
4986
4987	is_non_blocking = PQisnonblocking(pgsql);
4988
4989	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4990		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4991		RETURN_FALSE;
4992	}
4993
4994	if (_php_pgsql_link_has_results(pgsql)) {
4995		php_error_docref(NULL, E_NOTICE,
4996			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4997	}
4998
4999	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
5000	if (num_params > 0) {
5001		int i = 0;
5002		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5003
5004		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5005
5006			if (Z_TYPE_P(tmp) == IS_NULL) {
5007				params[i] = NULL;
5008			} else {
5009				zval tmp_val;
5010				ZVAL_COPY(&tmp_val, tmp);
5011				convert_to_string(&tmp_val);
5012				if (Z_TYPE(tmp_val) != IS_STRING) {
5013					php_error_docref(NULL, E_WARNING,"Error converting parameter");
5014					zval_ptr_dtor(&tmp_val);
5015					_php_pgsql_free_params(params, num_params);
5016					RETURN_FALSE;
5017				}
5018				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
5019				zval_ptr_dtor(&tmp_val);
5020			}
5021
5022			i++;
5023		} ZEND_HASH_FOREACH_END();
5024	}
5025
5026	if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5027		_php_pgsql_free_params(params, num_params);
5028	} else if (is_non_blocking) {
5029		_php_pgsql_free_params(params, num_params);
5030		RETURN_FALSE;
5031	} else {
5032		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5033			PQreset(pgsql);
5034		}
5035		if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5036			_php_pgsql_free_params(params, num_params);
5037			RETURN_FALSE;
5038		}
5039	}
5040
5041	if (is_non_blocking) {
5042		ret = PQflush(pgsql);
5043	} else {
5044		/* Wait to finish sending buffer */
5045		while ((ret = PQflush(pgsql))) {
5046			if (ret == -1) {
5047				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5048				break;
5049			}
5050			usleep(10000);
5051		}
5052
5053		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5054			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5055		}
5056	}
5057
5058	if (ret == 0) {
5059		RETURN_TRUE;
5060	} else if (ret == -1) {
5061		RETURN_FALSE;
5062	} else {
5063		RETURN_LONG(0);
5064	}
5065}
5066/* }}} */
5067#endif
5068
5069#if HAVE_PQSENDPREPARE
5070/* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
5071   Asynchronously prepare a query for future execution */
5072PHP_FUNCTION(pg_send_prepare)
5073{
5074	zval *pgsql_link;
5075	char *query, *stmtname;
5076	size_t stmtname_len, query_len;
5077	PGconn *pgsql;
5078	int is_non_blocking;
5079	int ret;
5080
5081	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
5082		return;
5083	}
5084
5085	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5086		RETURN_FALSE;
5087	}
5088
5089	is_non_blocking = PQisnonblocking(pgsql);
5090
5091	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5092		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5093		RETURN_FALSE;
5094	}
5095
5096	if (_php_pgsql_link_has_results(pgsql)) {
5097		php_error_docref(NULL, E_NOTICE,
5098			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5099	}
5100
5101	if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5102		if (is_non_blocking) {
5103			RETURN_FALSE;
5104		} else {
5105			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5106				PQreset(pgsql);
5107			}
5108			if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5109				RETURN_FALSE;
5110			}
5111		}
5112	}
5113
5114	if (is_non_blocking) {
5115		ret = PQflush(pgsql);
5116	} else {
5117		/* Wait to finish sending buffer */
5118		while ((ret = PQflush(pgsql))) {
5119			if (ret == -1) {
5120				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5121				break;
5122			}
5123			usleep(10000);
5124		}
5125		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5126			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5127		}
5128	}
5129
5130	if (ret == 0) {
5131		RETURN_TRUE;
5132	} else if (ret == -1) {
5133		RETURN_FALSE;
5134	} else {
5135		RETURN_LONG(0);
5136	}
5137}
5138/* }}} */
5139#endif
5140
5141#if HAVE_PQSENDQUERYPREPARED
5142/* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
5143   Executes prevriously prepared stmtname asynchronously */
5144PHP_FUNCTION(pg_send_execute)
5145{
5146	zval *pgsql_link;
5147	zval *pv_param_arr, *tmp;
5148	int num_params = 0;
5149	char **params = NULL;
5150	char *stmtname;
5151	size_t stmtname_len;
5152	PGconn *pgsql;
5153	int is_non_blocking;
5154	int ret;
5155
5156	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
5157		return;
5158	}
5159
5160	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5161		RETURN_FALSE;
5162	}
5163
5164	is_non_blocking = PQisnonblocking(pgsql);
5165
5166	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5167		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5168		RETURN_FALSE;
5169	}
5170
5171	if (_php_pgsql_link_has_results(pgsql)) {
5172		php_error_docref(NULL, E_NOTICE,
5173			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5174	}
5175
5176	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
5177	if (num_params > 0) {
5178		int i = 0;
5179		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5180
5181		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5182
5183			if (Z_TYPE_P(tmp) == IS_NULL) {
5184				params[i] = NULL;
5185			} else {
5186				zval tmp_val;
5187				ZVAL_COPY(&tmp_val, tmp);
5188				convert_to_string(&tmp_val);
5189				if (Z_TYPE(tmp_val) != IS_STRING) {
5190					php_error_docref(NULL, E_WARNING,"Error converting parameter");
5191					zval_ptr_dtor(&tmp_val);
5192					_php_pgsql_free_params(params, num_params);
5193					RETURN_FALSE;
5194				}
5195				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
5196				zval_ptr_dtor(&tmp_val);
5197			}
5198
5199			i++;
5200		} ZEND_HASH_FOREACH_END();
5201	}
5202
5203	if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5204		_php_pgsql_free_params(params, num_params);
5205	} else if (is_non_blocking) {
5206		_php_pgsql_free_params(params, num_params);
5207		RETURN_FALSE;
5208	} else {
5209		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5210			PQreset(pgsql);
5211		}
5212		if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5213			_php_pgsql_free_params(params, num_params);
5214			RETURN_FALSE;
5215		}
5216	}
5217
5218	if (is_non_blocking) {
5219		ret = PQflush(pgsql);
5220	} else {
5221		/* Wait to finish sending buffer */
5222		while ((ret = PQflush(pgsql))) {
5223			if (ret == -1) {
5224				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5225				break;
5226			}
5227			usleep(10000);
5228		}
5229		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5230			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5231		}
5232	}
5233
5234	if (ret == 0) {
5235		RETURN_TRUE;
5236	} else if (ret == -1) {
5237		RETURN_FALSE;
5238	} else {
5239		RETURN_LONG(0);
5240	}
5241}
5242/* }}} */
5243#endif
5244
5245/* {{{ proto resource pg_get_result(resource connection)
5246   Get asynchronous query result */
5247PHP_FUNCTION(pg_get_result)
5248{
5249	zval *pgsql_link;
5250	PGconn *pgsql;
5251	PGresult *pgsql_result;
5252	pgsql_result_handle *pg_result;
5253
5254	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5255		RETURN_FALSE;
5256	}
5257
5258	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5259		RETURN_FALSE;
5260	}
5261
5262	pgsql_result = PQgetResult(pgsql);
5263	if (!pgsql_result) {
5264		/* no result */
5265		RETURN_FALSE;
5266	}
5267	pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
5268	pg_result->conn = pgsql;
5269	pg_result->result = pgsql_result;
5270	pg_result->row = 0;
5271	RETURN_RES(zend_register_resource(pg_result, le_result));
5272}
5273/* }}} */
5274
5275/* {{{ proto mixed pg_result_status(resource result[, long result_type])
5276   Get status of query result */
5277PHP_FUNCTION(pg_result_status)
5278{
5279	zval *result;
5280	zend_long result_type = PGSQL_STATUS_LONG;
5281	ExecStatusType status;
5282	PGresult *pgsql_result;
5283	pgsql_result_handle *pg_result;
5284
5285	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5286								 &result, &result_type) == FAILURE) {
5287		RETURN_FALSE;
5288	}
5289
5290	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
5291		RETURN_FALSE;
5292	}
5293
5294	pgsql_result = pg_result->result;
5295	if (result_type == PGSQL_STATUS_LONG) {
5296		status = PQresultStatus(pgsql_result);
5297		RETURN_LONG((int)status);
5298	}
5299	else if (result_type == PGSQL_STATUS_STRING) {
5300		RETURN_STRING(PQcmdStatus(pgsql_result));
5301	}
5302	else {
5303		php_error_docref(NULL, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
5304		RETURN_FALSE;
5305	}
5306}
5307/* }}} */
5308
5309/* {{{ proto mixed pg_get_notify([resource connection[, int result_type]])
5310   Get asynchronous notification */
5311PHP_FUNCTION(pg_get_notify)
5312{
5313	zval *pgsql_link;
5314	zend_long result_type = PGSQL_ASSOC;
5315	PGconn *pgsql;
5316	PGnotify *pgsql_notify;
5317
5318	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5319								 &pgsql_link, &result_type) == FAILURE) {
5320		RETURN_FALSE;
5321	}
5322
5323	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5324		RETURN_FALSE;
5325	}
5326
5327	if (!(result_type & PGSQL_BOTH)) {
5328		php_error_docref(NULL, E_WARNING, "Invalid result type");
5329		RETURN_FALSE;
5330	}
5331
5332	PQconsumeInput(pgsql);
5333	pgsql_notify = PQnotifies(pgsql);
5334	if (!pgsql_notify) {
5335		/* no notify message */
5336		RETURN_FALSE;
5337	}
5338	array_init(return_value);
5339	if (result_type & PGSQL_NUM) {
5340		add_index_string(return_value, 0, pgsql_notify->relname);
5341		add_index_long(return_value, 1, pgsql_notify->be_pid);
5342#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5343		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5344#else
5345		if (atof(PG_VERSION) >= 9.0) {
5346#endif
5347#if HAVE_PQPARAMETERSTATUS
5348			add_index_string(return_value, 2, pgsql_notify->extra);
5349#endif
5350		}
5351	}
5352	if (result_type & PGSQL_ASSOC) {
5353		add_assoc_string(return_value, "message", pgsql_notify->relname);
5354		add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
5355#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5356		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5357#else
5358		if (atof(PG_VERSION) >= 9.0) {
5359#endif
5360#if HAVE_PQPARAMETERSTATUS
5361			add_assoc_string(return_value, "payload", pgsql_notify->extra);
5362#endif
5363		}
5364	}
5365	PQfreemem(pgsql_notify);
5366}
5367/* }}} */
5368
5369/* {{{ proto int pg_get_pid([resource connection)
5370   Get backend(server) pid */
5371PHP_FUNCTION(pg_get_pid)
5372{
5373	zval *pgsql_link;
5374	PGconn *pgsql;
5375
5376	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
5377								 &pgsql_link) == FAILURE) {
5378		RETURN_FALSE;
5379	}
5380
5381	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5382		RETURN_FALSE;
5383	}
5384
5385	RETURN_LONG(PQbackendPID(pgsql));
5386}
5387/* }}} */
5388
5389static size_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
5390{
5391	return 0;
5392}
5393/* }}} */
5394
5395static size_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count) /* {{{ */
5396{
5397	return 0;
5398}
5399/* }}} */
5400
5401static int php_pgsql_fd_close(php_stream *stream, int close_handle) /* {{{ */
5402{
5403	return EOF;
5404}
5405/* }}} */
5406
5407static int php_pgsql_fd_flush(php_stream *stream) /* {{{ */
5408{
5409	return FAILURE;
5410}
5411/* }}} */
5412
5413static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
5414{
5415	PGconn *pgsql = (PGconn *) stream->abstract;
5416	switch (option) {
5417		case PHP_STREAM_OPTION_BLOCKING:
5418			return PQ_SETNONBLOCKING(pgsql, value);
5419		default:
5420			return FAILURE;
5421	}
5422}
5423/* }}} */
5424
5425static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ */
5426{
5427	PGconn *pgsql = (PGconn *) stream->abstract;
5428	int fd_number;
5429
5430	switch (cast_as)	{
5431		case PHP_STREAM_AS_FD_FOR_SELECT:
5432		case PHP_STREAM_AS_FD:
5433		case PHP_STREAM_AS_SOCKETD:
5434			if (ret) {
5435				fd_number = PQsocket(pgsql);
5436				if (fd_number == -1) {
5437					return FAILURE;
5438				}
5439
5440				*(php_socket_t *)ret = fd_number;
5441				return SUCCESS;
5442			}
5443		default:
5444			return FAILURE;
5445	}
5446}
5447/* }}} */
5448
5449/* {{{ proto resource pg_socket(resource connection)
5450   Get a read-only handle to the socket underlying the pgsql connection */
5451PHP_FUNCTION(pg_socket)
5452{
5453	zval *pgsql_link;
5454	php_stream *stream;
5455	PGconn *pgsql;
5456
5457	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5458		return;
5459	}
5460
5461	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5462		RETURN_FALSE;
5463	}
5464
5465	stream = php_stream_alloc(&php_stream_pgsql_fd_ops, pgsql, NULL, "r");
5466
5467	if (stream) {
5468		php_stream_to_zval(stream, return_value);
5469		return;
5470	}
5471
5472	RETURN_FALSE;
5473}
5474/* }}} */
5475
5476/* {{{ proto bool pg_consume_input(resource connection)
5477   Reads input on the connection */
5478PHP_FUNCTION(pg_consume_input)
5479{
5480	zval *pgsql_link;
5481	PGconn *pgsql;
5482
5483	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5484		return;
5485	}
5486
5487	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5488		RETURN_FALSE;
5489	}
5490
5491	RETURN_BOOL(PQconsumeInput(pgsql));
5492}
5493/* }}} */
5494
5495/* {{{ proto mixed pg_flush(resource connection)
5496   Flush outbound query data on the connection */
5497PHP_FUNCTION(pg_flush)
5498{
5499	zval *pgsql_link;
5500	PGconn *pgsql;
5501	int ret;
5502	int is_non_blocking;
5503
5504	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5505		return;
5506	}
5507
5508	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5509		RETURN_FALSE;
5510	}
5511
5512	is_non_blocking = PQisnonblocking(pgsql);
5513
5514	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5515		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5516		RETURN_FALSE;
5517	}
5518
5519	ret = PQflush(pgsql);
5520
5521	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 0) == -1) {
5522		php_error_docref(NULL, E_NOTICE, "Failed resetting connection to blocking mode");
5523	}
5524
5525	switch (ret) {
5526		case 0: RETURN_TRUE; break;
5527		case 1: RETURN_LONG(0); break;
5528		default: RETURN_FALSE;
5529	}
5530}
5531/* }}} */
5532
5533/* {{{ php_pgsql_meta_data
5534 * TODO: Add meta_data cache for better performance
5535 */
5536PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended)
5537{
5538	PGresult *pg_result;
5539	char *src, *tmp_name, *tmp_name2 = NULL;
5540	char *escaped;
5541	smart_str querystr = {0};
5542	size_t new_len;
5543	int i, num_rows;
5544	zval elem;
5545
5546	if (!*table_name) {
5547		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5548		return FAILURE;
5549	}
5550
5551	src = estrdup(table_name);
5552	tmp_name = php_strtok_r(src, ".", &tmp_name2);
5553	if (!tmp_name) {
5554		efree(src);
5555		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5556		return FAILURE;
5557	}
5558	if (!tmp_name2 || !*tmp_name2) {
5559		/* Default schema */
5560		tmp_name2 = tmp_name;
5561		tmp_name = "public";
5562	}
5563
5564	if (extended) {
5565		smart_str_appends(&querystr,
5566						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, "
5567						  "d.description "
5568						  "FROM pg_class as c "
5569						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5570						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5571						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5572						  " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) "
5573						  "WHERE a.attnum > 0  AND c.relname = '");
5574	} else {
5575		smart_str_appends(&querystr,
5576						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype "
5577						  "FROM pg_class as c "
5578						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5579						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5580						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5581						  "WHERE a.attnum > 0 AND c.relname = '");
5582	}
5583	escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1);
5584	new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL);
5585	if (new_len) {
5586		smart_str_appendl(&querystr, escaped, new_len);
5587	}
5588	efree(escaped);
5589
5590	smart_str_appends(&querystr, "' AND n.nspname = '");
5591	escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1);
5592	new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL);
5593	if (new_len) {
5594		smart_str_appendl(&querystr, escaped, new_len);
5595	}
5596	efree(escaped);
5597
5598	smart_str_appends(&querystr, "' ORDER BY a.attnum;");
5599	smart_str_0(&querystr);
5600	efree(src);
5601
5602	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
5603	if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
5604		php_error_docref(NULL, E_WARNING, "Table '%s' doesn't exists", table_name);
5605		smart_str_free(&querystr);
5606		PQclear(pg_result);
5607		return FAILURE;
5608	}
5609	smart_str_free(&querystr);
5610
5611	for (i = 0; i < num_rows; i++) {
5612		char *name;
5613		array_init(&elem);
5614		/* pg_attribute.attnum */
5615		add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1)));
5616		/* pg_type.typname */
5617		add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2));
5618		/* pg_attribute.attlen */
5619		add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result,i,3)));
5620		/* pg_attribute.attnonull */
5621		add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t"));
5622		/* pg_attribute.atthasdef */
5623		add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result,i,5), "t"));
5624		/* pg_attribute.attndims */
5625		add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6)));
5626		/* pg_type.typtype */
5627		add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e"));
5628		if (extended) {
5629			/* pg_type.typtype */
5630			add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b"));
5631			add_assoc_bool_ex(&elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c"));
5632			add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p"));
5633			/* pg_description.description */
5634			add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8));
5635		}
5636		/* pg_attribute.attname */
5637		name = PQgetvalue(pg_result,i,0);
5638		add_assoc_zval(meta, name, &elem);
5639	}
5640	PQclear(pg_result);
5641
5642	return SUCCESS;
5643}
5644
5645/* }}} */
5646
5647/* {{{ proto array pg_meta_data(resource db, string table [, bool extended])
5648   Get meta_data */
5649PHP_FUNCTION(pg_meta_data)
5650{
5651	zval *pgsql_link;
5652	char *table_name;
5653	size_t table_name_len;
5654	zend_bool extended=0;
5655	PGconn *pgsql;
5656
5657	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|b",
5658							  &pgsql_link, &table_name, &table_name_len, &extended) == FAILURE) {
5659		return;
5660	}
5661
5662	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5663		RETURN_FALSE;
5664	}
5665
5666	array_init(return_value);
5667	if (php_pgsql_meta_data(pgsql, table_name, return_value, extended) == FAILURE) {
5668		zval_dtor(return_value); /* destroy array */
5669		RETURN_FALSE;
5670	}
5671}
5672/* }}} */
5673
5674/* {{{ php_pgsql_get_data_type
5675 */
5676static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
5677{
5678	/* This is stupid way to do. I'll fix it when I decied how to support
5679	   user defined types. (Yasuo) */
5680
5681	/* boolean */
5682	if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean"))
5683		return PG_BOOL;
5684	/* object id */
5685	if (!strcmp(type_name, "oid"))
5686		return PG_OID;
5687	/* integer */
5688	if (!strcmp(type_name, "int2") || !strcmp(type_name, "smallint"))
5689		return PG_INT2;
5690	if (!strcmp(type_name, "int4") || !strcmp(type_name, "integer"))
5691		return PG_INT4;
5692	if (!strcmp(type_name, "int8") || !strcmp(type_name, "bigint"))
5693		return PG_INT8;
5694	/* real and other */
5695	if (!strcmp(type_name, "float4") || !strcmp(type_name, "real"))
5696		return PG_FLOAT4;
5697	if (!strcmp(type_name, "float8") || !strcmp(type_name, "double precision"))
5698		return PG_FLOAT8;
5699	if (!strcmp(type_name, "numeric"))
5700		return PG_NUMERIC;
5701	if (!strcmp(type_name, "money"))
5702		return PG_MONEY;
5703	/* character */
5704	if (!strcmp(type_name, "text"))
5705		return PG_TEXT;
5706	if (!strcmp(type_name, "bpchar") || !strcmp(type_name, "character"))
5707		return PG_CHAR;
5708	if (!strcmp(type_name, "varchar") || !strcmp(type_name, "character varying"))
5709		return PG_VARCHAR;
5710	/* time and interval */
5711	if (!strcmp(type_name, "abstime"))
5712		return PG_UNIX_TIME;
5713	if (!strcmp(type_name, "reltime"))
5714		return PG_UNIX_TIME_INTERVAL;
5715	if (!strcmp(type_name, "tinterval"))
5716		return PG_UNIX_TIME_INTERVAL;
5717	if (!strcmp(type_name, "date"))
5718		return PG_DATE;
5719	if (!strcmp(type_name, "time"))
5720		return PG_TIME;
5721	if (!strcmp(type_name, "time with time zone") || !strcmp(type_name, "timetz"))
5722		return PG_TIME_WITH_TIMEZONE;
5723	if (!strcmp(type_name, "timestamp without time zone") || !strcmp(type_name, "timestamp"))
5724		return PG_TIMESTAMP;
5725	if (!strcmp(type_name, "timestamp with time zone") || !strcmp(type_name, "timestamptz"))
5726		return PG_TIMESTAMP_WITH_TIMEZONE;
5727	if (!strcmp(type_name, "interval"))
5728		return PG_INTERVAL;
5729	/* binary */
5730	if (!strcmp(type_name, "bytea"))
5731		return PG_BYTEA;
5732	/* network */
5733	if (!strcmp(type_name, "cidr"))
5734		return PG_CIDR;
5735	if (!strcmp(type_name, "inet"))
5736		return PG_INET;
5737	if (!strcmp(type_name, "macaddr"))
5738		return PG_MACADDR;
5739	/* bit */
5740	if (!strcmp(type_name, "bit"))
5741		return PG_BIT;
5742	if (!strcmp(type_name, "bit varying"))
5743		return PG_VARBIT;
5744	/* geometric */
5745	if (!strcmp(type_name, "line"))
5746		return PG_LINE;
5747	if (!strcmp(type_name, "lseg"))
5748		return PG_LSEG;
5749	if (!strcmp(type_name, "box"))
5750		return PG_BOX;
5751	if (!strcmp(type_name, "path"))
5752		return PG_PATH;
5753	if (!strcmp(type_name, "point"))
5754		return PG_POINT;
5755	if (!strcmp(type_name, "polygon"))
5756		return PG_POLYGON;
5757	if (!strcmp(type_name, "circle"))
5758		return PG_CIRCLE;
5759
5760	return PG_UNKNOWN;
5761}
5762/* }}} */
5763
5764/* {{{ php_pgsql_convert_match
5765 * test field value with regular expression specified.
5766 */
5767static int php_pgsql_convert_match(const char *str, size_t str_len, const char *regex , int icase)
5768{
5769	pcre *re;
5770	const char *err_msg;
5771	int err_offset;
5772	int options = PCRE_NO_AUTO_CAPTURE, res;
5773	size_t i;
5774
5775	/* Check invalid chars for POSIX regex */
5776	for (i = 0; i < str_len; i++) {
5777		if (str[i] == '\n' ||
5778			str[i] == '\r' ||
5779			str[i] == '\0' ) {
5780			return FAILURE;
5781		}
5782	}
5783
5784	if (icase) {
5785		options |= PCRE_CASELESS;
5786	}
5787
5788	if ((re = pcre_compile(regex, options, &err_msg, &err_offset, NULL)) == NULL) {
5789		php_error_docref(NULL, E_WARNING, "Cannot compile regex");
5790		return FAILURE;
5791	}
5792
5793	res = pcre_exec(re, NULL, str, str_len, 0, 0, NULL, 0);
5794	pcre_free(re);
5795
5796	if (res == PCRE_ERROR_NOMATCH) {
5797		return FAILURE;
5798	} else if (res) {
5799		php_error_docref(NULL, E_WARNING, "Cannot exec regex");
5800		return FAILURE;
5801	}
5802	return SUCCESS;
5803}
5804
5805/* }}} */
5806
5807/* {{{ php_pgsql_add_quote
5808 * add quotes around string.
5809 */
5810static int php_pgsql_add_quotes(zval *src, zend_bool should_free)
5811{
5812	smart_str str = {0};
5813
5814	assert(Z_TYPE_P(src) == IS_STRING);
5815	assert(should_free == 1 || should_free == 0);
5816
5817	smart_str_appendc(&str, 'E');
5818	smart_str_appendc(&str, '\'');
5819	smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
5820	smart_str_appendc(&str, '\'');
5821	smart_str_0(&str);
5822
5823	if (should_free) {
5824		zval_ptr_dtor(src);
5825	}
5826	ZVAL_NEW_STR(src, str.s);
5827
5828	return SUCCESS;
5829}
5830/* }}} */
5831
5832#define PGSQL_CONV_CHECK_IGNORE() \
5833	if (!err && Z_TYPE(new_val) == IS_STRING && !strcmp(Z_STRVAL(new_val), "NULL")) { \
5834		/* if new_value is string "NULL" and field has default value, remove element to use default value */ \
5835		if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_TYPE_P(has_default) == IS_TRUE) { \
5836			zval_ptr_dtor(&new_val); \
5837			skip_field = 1; \
5838		} \
5839		/* raise error if it's not null and cannot be ignored */ \
5840		else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_TYPE_P(not_null) == IS_TRUE) { \
5841			php_error_docref(NULL, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", ZSTR_VAL(field)); \
5842			err = 1; \
5843		} \
5844	}
5845
5846/* {{{ php_pgsql_convert
5847 * check and convert array values (fieldname=>vlaue pair) for sql
5848 */
5849PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, zend_ulong opt)
5850{
5851	zend_string *field = NULL;
5852	zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val;
5853	int err = 0, skip_field;
5854	php_pgsql_data_type data_type;
5855
5856	assert(pg_link != NULL);
5857	assert(Z_TYPE_P(values) == IS_ARRAY);
5858	assert(Z_TYPE_P(result) == IS_ARRAY);
5859	assert(!(opt & ~PGSQL_CONV_OPTS));
5860
5861	if (!table_name) {
5862		return FAILURE;
5863	}
5864
5865	array_init(&meta);
5866/* table_name is escaped by php_pgsql_meta_data */
5867	if (php_pgsql_meta_data(pg_link, table_name, &meta, 0) == FAILURE) {
5868		zval_ptr_dtor(&meta);
5869		return FAILURE;
5870	}
5871
5872	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(values), field, val) {
5873		skip_field = 0;
5874		ZVAL_NULL(&new_val);
5875
5876		if (!err && field == NULL) {
5877			php_error_docref(NULL, E_WARNING, "Accepts only string key for values");
5878			err = 1;
5879		}
5880
5881		if (!err && (def = zend_hash_find(Z_ARRVAL(meta), field)) == NULL) {
5882			php_error_docref(NULL, E_NOTICE, "Invalid field name (%s) in values", ZSTR_VAL(field));
5883			err = 1;
5884		}
5885		if (!err && (type = zend_hash_str_find(Z_ARRVAL_P(def), "type", sizeof("type") - 1)) == NULL) {
5886			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'type'");
5887			err = 1;
5888		}
5889		if (!err && (not_null = zend_hash_str_find(Z_ARRVAL_P(def), "not null", sizeof("not null") - 1)) == NULL) {
5890			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'not null'");
5891			err = 1;
5892		}
5893		if (!err && (has_default = zend_hash_str_find(Z_ARRVAL_P(def), "has default", sizeof("has default") - 1)) == NULL) {
5894			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'has default'");
5895			err = 1;
5896		}
5897		if (!err && (is_enum = zend_hash_str_find(Z_ARRVAL_P(def), "is enum", sizeof("is enum") - 1)) == NULL) {
5898			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'is enum'");
5899			err = 1;
5900		}
5901		if (!err && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT)) {
5902			php_error_docref(NULL, E_NOTICE, "Expects scalar values as field values");
5903			err = 1;
5904		}
5905		if (err) {
5906			break; /* break out for() */
5907		}
5908
5909		convert_to_boolean(is_enum);
5910		if (Z_TYPE_P(is_enum) == IS_TRUE) {
5911			/* enums need to be treated like strings */
5912			data_type = PG_TEXT;
5913		} else {
5914			data_type = php_pgsql_get_data_type(Z_STRVAL_P(type), Z_STRLEN_P(type));
5915		}
5916
5917		switch(data_type)
5918		{
5919			case PG_BOOL:
5920				switch (Z_TYPE_P(val)) {
5921					case IS_STRING:
5922						if (Z_STRLEN_P(val) == 0) {
5923							ZVAL_STRING(&new_val, "NULL");
5924						}
5925						else {
5926							if (!strcmp(Z_STRVAL_P(val), "t") || !strcmp(Z_STRVAL_P(val), "T") ||
5927								!strcmp(Z_STRVAL_P(val), "y") || !strcmp(Z_STRVAL_P(val), "Y") ||
5928								!strcmp(Z_STRVAL_P(val), "true") || !strcmp(Z_STRVAL_P(val), "True") ||
5929								!strcmp(Z_STRVAL_P(val), "yes") || !strcmp(Z_STRVAL_P(val), "Yes") ||
5930								!strcmp(Z_STRVAL_P(val), "1")) {
5931								ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5932							}
5933							else if (!strcmp(Z_STRVAL_P(val), "f") || !strcmp(Z_STRVAL_P(val), "F") ||
5934									 !strcmp(Z_STRVAL_P(val), "n") || !strcmp(Z_STRVAL_P(val), "N") ||
5935									 !strcmp(Z_STRVAL_P(val), "false") ||  !strcmp(Z_STRVAL_P(val), "False") ||
5936									 !strcmp(Z_STRVAL_P(val), "no") ||  !strcmp(Z_STRVAL_P(val), "No") ||
5937									 !strcmp(Z_STRVAL_P(val), "0")) {
5938								ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5939							}
5940							else {
5941								php_error_docref(NULL, E_NOTICE, "Detected invalid value (%s) for PostgreSQL %s field (%s)", Z_STRVAL_P(val), Z_STRVAL_P(type), ZSTR_VAL(field));
5942								err = 1;
5943							}
5944						}
5945						break;
5946
5947					case IS_LONG:
5948						if (Z_LVAL_P(val)) {
5949							ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5950						}
5951						else {
5952							ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5953						}
5954						break;
5955
5956					case IS_TRUE:
5957						ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5958						break;
5959
5960					case IS_FALSE:
5961						ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5962						break;
5963
5964					case IS_NULL:
5965						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5966						break;
5967
5968					default:
5969						err = 1;
5970				}
5971				PGSQL_CONV_CHECK_IGNORE();
5972				if (err) {
5973					php_error_docref(NULL, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
5974				}
5975				break;
5976
5977			case PG_OID:
5978			case PG_INT2:
5979			case PG_INT4:
5980			case PG_INT8:
5981				switch (Z_TYPE_P(val)) {
5982					case IS_STRING:
5983						if (Z_STRLEN_P(val) == 0) {
5984							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5985						}
5986						else {
5987							/* FIXME: better regex must be used */
5988							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([+-]{0,1}[0-9]+)$", 0) == FAILURE) {
5989								err = 1;
5990							}
5991							else {
5992								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
5993							}
5994						}
5995						break;
5996
5997					case IS_DOUBLE:
5998						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
5999						convert_to_long_ex(&new_val);
6000						break;
6001
6002					case IS_LONG:
6003						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6004						break;
6005
6006					case IS_NULL:
6007						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6008						break;
6009
6010					default:
6011						err = 1;
6012				}
6013				PGSQL_CONV_CHECK_IGNORE();
6014				if (err) {
6015					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for pgsql '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6016				}
6017				break;
6018
6019			case PG_NUMERIC:
6020			case PG_MONEY:
6021			case PG_FLOAT4:
6022			case PG_FLOAT8:
6023				switch (Z_TYPE_P(val)) {
6024					case IS_STRING:
6025						if (Z_STRLEN_P(val) == 0) {
6026							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6027						}
6028						else {
6029							/* better regex? */
6030							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$", 0) == FAILURE) {
6031								if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[+-]{0,1}(inf)(inity){0,1}$", 1) == FAILURE) {
6032									err = 1;
6033								} else {
6034									ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6035									php_pgsql_add_quotes(&new_val, 1);
6036								}
6037							}
6038							else {
6039								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6040							}
6041						}
6042						break;
6043
6044					case IS_LONG:
6045						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6046						break;
6047
6048					case IS_DOUBLE:
6049						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6050						break;
6051
6052					case IS_NULL:
6053						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6054						break;
6055
6056					default:
6057						err = 1;
6058				}
6059				PGSQL_CONV_CHECK_IGNORE();
6060				if (err) {
6061					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6062				}
6063				break;
6064
6065				/* Exotic types are handled as string also.
6066				   Please feel free to add more valitions. Invalid query fails
6067				   at execution anyway. */
6068			case PG_TEXT:
6069			case PG_CHAR:
6070			case PG_VARCHAR:
6071				/* bit */
6072			case PG_BIT:
6073			case PG_VARBIT:
6074				/* geometric */
6075			case PG_LINE:
6076			case PG_LSEG:
6077			case PG_POINT:
6078			case PG_BOX:
6079			case PG_PATH:
6080			case PG_POLYGON:
6081			case PG_CIRCLE:
6082				/* unknown. JSON, Array etc */
6083			case PG_UNKNOWN:
6084				switch (Z_TYPE_P(val)) {
6085					case IS_STRING:
6086						if (Z_STRLEN_P(val) == 0) {
6087							if (opt & PGSQL_CONV_FORCE_NULL) {
6088								ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6089							} else {
6090								ZVAL_STRINGL(&new_val, "''", sizeof("''")-1);
6091							}
6092						}
6093						else {
6094							zend_string *str;
6095							/* PostgreSQL ignores \0 */
6096							str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0);
6097							/* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */
6098							ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6099							str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6100							ZVAL_NEW_STR(&new_val, str);
6101							php_pgsql_add_quotes(&new_val, 1);
6102						}
6103						break;
6104
6105					case IS_LONG:
6106						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6107						convert_to_string_ex(&new_val);
6108						break;
6109
6110					case IS_DOUBLE:
6111						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6112						convert_to_string_ex(&new_val);
6113						break;
6114
6115					case IS_NULL:
6116						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6117						break;
6118
6119					default:
6120						err = 1;
6121				}
6122				PGSQL_CONV_CHECK_IGNORE();
6123				if (err) {
6124					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6125				}
6126				break;
6127
6128			case PG_UNIX_TIME:
6129			case PG_UNIX_TIME_INTERVAL:
6130				/* these are the actallay a integer */
6131				switch (Z_TYPE_P(val)) {
6132					case IS_STRING:
6133						if (Z_STRLEN_P(val) == 0) {
6134							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6135						}
6136						else {
6137							/* better regex? */
6138							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[0-9]+$", 0) == FAILURE) {
6139								err = 1;
6140							}
6141							else {
6142								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6143								convert_to_long_ex(&new_val);
6144							}
6145						}
6146						break;
6147
6148					case IS_DOUBLE:
6149						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6150						convert_to_long_ex(&new_val);
6151						break;
6152
6153					case IS_LONG:
6154						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6155						break;
6156
6157					case IS_NULL:
6158						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6159						break;
6160
6161					default:
6162						err = 1;
6163				}
6164				PGSQL_CONV_CHECK_IGNORE();
6165				if (err) {
6166					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6167				}
6168				break;
6169
6170			case PG_CIDR:
6171			case PG_INET:
6172				switch (Z_TYPE_P(val)) {
6173					case IS_STRING:
6174						if (Z_STRLEN_P(val) == 0) {
6175							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6176						}
6177						else {
6178							/* The inet type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field. See more in the doc.
6179							 	The regex might still be not perfect, but catches the most of IP variants. We might decide to remove the regex
6180								at all though and let the server side to handle it.*/
6181							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])(\\/[0-9]{1,3})?$", 0) == FAILURE
6182								&& php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\\/[0-9]{1,3})?$", 0) == FAILURE) {
6183								err = 1;
6184							}
6185							else {
6186								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6187								php_pgsql_add_quotes(&new_val, 1);
6188							}
6189						}
6190						break;
6191
6192					case IS_NULL:
6193						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6194						break;
6195
6196					default:
6197						err = 1;
6198				}
6199				PGSQL_CONV_CHECK_IGNORE();
6200				if (err) {
6201					php_error_docref(NULL, E_NOTICE, "Expects NULL or IPv4 or IPv6 address string for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6202				}
6203				break;
6204
6205			case PG_TIME_WITH_TIMEZONE:
6206			case PG_TIMESTAMP:
6207			case PG_TIMESTAMP_WITH_TIMEZONE:
6208				switch(Z_TYPE_P(val)) {
6209					case IS_STRING:
6210						if (Z_STRLEN_P(val) == 0) {
6211							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6212						} else if (!strcasecmp(Z_STRVAL_P(val), "now()")) {
6213							ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
6214						} else {
6215							/* better regex? */
6216							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})(([ \\t]+|T)(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$", 1) == FAILURE) {
6217								err = 1;
6218							} else {
6219								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6220								php_pgsql_add_quotes(&new_val, 1);
6221							}
6222						}
6223						break;
6224
6225					case IS_NULL:
6226						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6227						break;
6228
6229					default:
6230						err = 1;
6231				}
6232				PGSQL_CONV_CHECK_IGNORE();
6233				if (err) {
6234					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6235				}
6236				break;
6237
6238			case PG_DATE:
6239				switch(Z_TYPE_P(val)) {
6240					case IS_STRING:
6241						if (Z_STRLEN_P(val) == 0) {
6242							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6243						}
6244						else {
6245							/* FIXME: better regex must be used */
6246							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$", 1) == FAILURE) {
6247								err = 1;
6248							}
6249							else {
6250								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6251								php_pgsql_add_quotes(&new_val, 1);
6252							}
6253						}
6254						break;
6255
6256					case IS_NULL:
6257						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6258						break;
6259
6260					default:
6261						err = 1;
6262				}
6263				PGSQL_CONV_CHECK_IGNORE();
6264				if (err) {
6265					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6266				}
6267				break;
6268
6269			case PG_TIME:
6270				switch(Z_TYPE_P(val)) {
6271					case IS_STRING:
6272						if (Z_STRLEN_P(val) == 0) {
6273							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6274						}
6275						else {
6276							/* FIXME: better regex must be used */
6277							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1})){0,1}$", 1) == FAILURE) {
6278								err = 1;
6279							}
6280							else {
6281								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6282								php_pgsql_add_quotes(&new_val, 1);
6283							}
6284						}
6285						break;
6286
6287					case IS_NULL:
6288						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6289						break;
6290
6291					default:
6292						err = 1;
6293				}
6294				PGSQL_CONV_CHECK_IGNORE();
6295				if (err) {
6296					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6297				}
6298				break;
6299
6300			case PG_INTERVAL:
6301				switch(Z_TYPE_P(val)) {
6302					case IS_STRING:
6303						if (Z_STRLEN_P(val) == 0) {
6304							ZVAL_STRING(&new_val, "NULL");
6305						}
6306						else {
6307
6308							/* From the Postgres docs:
6309
6310							   interval values can be written with the following syntax:
6311							   [@] quantity unit [quantity unit...] [direction]
6312
6313							   Where: quantity is a number (possibly signed); unit is second, minute, hour,
6314							   day, week, month, year, decade, century, millennium, or abbreviations or
6315							   plurals of these units [note not *all* abbreviations] ; direction can be
6316							   ago or empty. The at sign (@) is optional noise.
6317
6318							   ...
6319
6320							   Quantities of days, hours, minutes, and seconds can be specified without explicit
6321							   unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
6322							   sec'.
6323							*/
6324							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val),
6325														"^(@?[ \\t]+)?("
6326
6327														/* Textual time units and their abbreviations: */
6328														"(([-+]?[ \\t]+)?"
6329														"[0-9]+(\\.[0-9]*)?[ \\t]*"
6330														"(millenniums|millennia|millennium|mil|mils|"
6331														"centuries|century|cent|c|"
6332														"decades|decade|dec|decs|"
6333														"years|year|y|"
6334														"months|month|mon|"
6335														"weeks|week|w|"
6336														"days|day|d|"
6337														"hours|hour|hr|hrs|h|"
6338														"minutes|minute|mins|min|m|"
6339														"seconds|second|secs|sec|s))+|"
6340
6341														/* Textual time units plus (dd)* hh[:mm[:ss]] */
6342														"((([-+]?[ \\t]+)?"
6343														"[0-9]+(\\.[0-9]*)?[ \\t]*"
6344														"(millenniums|millennia|millennium|mil|mils|"
6345														"centuries|century|cent|c|"
6346														"decades|decade|dec|decs|"
6347														"years|year|y|"
6348														"months|month|mon|"
6349														"weeks|week|w|"
6350														"days|day|d))+"
6351														"([-+]?[ \\t]+"
6352														"([0-9]+[ \\t]+)+"				 /* dd */
6353														"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */
6354														")?))"
6355														"([ \\t]+ago)?$",
6356														1) == FAILURE) {
6357								err = 1;
6358							}
6359							else {
6360								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6361								php_pgsql_add_quotes(&new_val, 1);
6362							}
6363						}
6364						break;
6365
6366					case IS_NULL:
6367						ZVAL_STRING(&new_val, "NULL");
6368						break;
6369
6370					default:
6371						err = 1;
6372				}
6373				PGSQL_CONV_CHECK_IGNORE();
6374				if (err) {
6375					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6376				}
6377				break;
6378#ifdef HAVE_PQESCAPE
6379			case PG_BYTEA:
6380				switch (Z_TYPE_P(val)) {
6381					case IS_STRING:
6382						if (Z_STRLEN_P(val) == 0) {
6383							ZVAL_STRING(&new_val, "NULL");
6384						}
6385						else {
6386							unsigned char *tmp;
6387							size_t to_len;
6388							smart_str s = {0};
6389#ifdef HAVE_PQESCAPE_BYTEA_CONN
6390							tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len);
6391#else
6392							tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len);
6393#endif
6394							ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */
6395							PQfreemem(tmp);
6396							php_pgsql_add_quotes(&new_val, 1);
6397							smart_str_appendl(&s, Z_STRVAL(new_val), Z_STRLEN(new_val));
6398							smart_str_0(&s);
6399							zval_ptr_dtor(&new_val);
6400							ZVAL_NEW_STR(&new_val, s.s);
6401						}
6402						break;
6403
6404					case IS_LONG:
6405						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6406						convert_to_string_ex(&new_val);
6407						break;
6408
6409					case IS_DOUBLE:
6410						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6411						convert_to_string_ex(&new_val);
6412						break;
6413
6414					case IS_NULL:
6415						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6416						break;
6417
6418					default:
6419						err = 1;
6420				}
6421				PGSQL_CONV_CHECK_IGNORE();
6422				if (err) {
6423					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6424				}
6425				break;
6426
6427#endif
6428			case PG_MACADDR:
6429				switch(Z_TYPE_P(val)) {
6430					case IS_STRING:
6431						if (Z_STRLEN_P(val) == 0) {
6432							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6433						}
6434						else {
6435							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$", 1) == FAILURE) {
6436								err = 1;
6437							}
6438							else {
6439								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6440								php_pgsql_add_quotes(&new_val, 1);
6441							}
6442						}
6443						break;
6444
6445					case IS_NULL:
6446						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6447						break;
6448
6449					default:
6450						err = 1;
6451				}
6452				PGSQL_CONV_CHECK_IGNORE();
6453				if (err) {
6454					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6455				}
6456				break;
6457
6458			default:
6459				/* should not happen */
6460				php_error_docref(NULL, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_P(type), ZSTR_VAL(field));
6461				err = 1;
6462				break;
6463		} /* switch */
6464
6465		if (err) {
6466			zval_ptr_dtor(&new_val);
6467			break; /* break out for() */
6468		}
6469		/* If field is NULL and HAS DEFAULT, should be skipped */
6470		if (!skip_field) {
6471			char *escaped;
6472
6473			if (_php_pgsql_detect_identifier_escape(ZSTR_VAL(field), ZSTR_LEN(field)) == SUCCESS) {
6474				zend_hash_update(Z_ARRVAL_P(result), field, &new_val);
6475			} else {
6476				escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field));
6477				add_assoc_zval(result, escaped, &new_val);
6478				PGSQLfree(escaped);
6479			}
6480		}
6481	} ZEND_HASH_FOREACH_END(); /* for */
6482
6483	zval_ptr_dtor(&meta);
6484
6485	if (err) {
6486		/* shouldn't destroy & free zval here */
6487		return FAILURE;
6488	}
6489	return SUCCESS;
6490}
6491/* }}} */
6492
6493/* {{{ proto array pg_convert(resource db, string table, array values[, int options])
6494   Check and convert values for PostgreSQL SQL statement */
6495PHP_FUNCTION(pg_convert)
6496{
6497	zval *pgsql_link, *values;
6498	char *table_name;
6499	size_t table_name_len;
6500	zend_ulong option = 0;
6501	PGconn *pg_link;
6502
6503	if (zend_parse_parameters(ZEND_NUM_ARGS(),
6504							  "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
6505		return;
6506	}
6507	if (option & ~PGSQL_CONV_OPTS) {
6508		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6509		RETURN_FALSE;
6510	}
6511	if (!table_name_len) {
6512		php_error_docref(NULL, E_NOTICE, "Table name is invalid");
6513		RETURN_FALSE;
6514	}
6515
6516	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6517		RETURN_FALSE;
6518	}
6519
6520	if (php_pgsql_flush_query(pg_link)) {
6521		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6522	}
6523	array_init(return_value);
6524	if (php_pgsql_convert(pg_link, table_name, values, return_value, option) == FAILURE) {
6525		zval_dtor(return_value);
6526		RETURN_FALSE;
6527	}
6528}
6529/* }}} */
6530
6531static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
6532{
6533	if (opt & PGSQL_DML_ASYNC) {
6534		if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
6535			return 0;
6536		}
6537	}
6538	else {
6539		PGresult *pg_result;
6540
6541		pg_result = PQexec(pg_link, ZSTR_VAL(querystr->s));
6542		if (PQresultStatus(pg_result) == expect) {
6543			PQclear(pg_result);
6544			return 0;
6545		} else {
6546			php_error_docref(NULL, E_WARNING, "%s", PQresultErrorMessage(pg_result));
6547			PQclear(pg_result);
6548		}
6549	}
6550
6551	return -1;
6552}
6553/* }}} */
6554
6555static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */
6556{
6557	char *table_copy, *escaped, *tmp;
6558	const char *token;
6559	size_t len;
6560
6561	/* schame.table should be "schame"."table" */
6562	table_copy = estrdup(table);
6563	token = php_strtok_r(table_copy, ".", &tmp);
6564	if (token == NULL) {
6565		token = table;
6566	}
6567	len = strlen(token);
6568	if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) {
6569		smart_str_appendl(querystr, token, len);
6570	} else {
6571		escaped = PGSQLescapeIdentifier(pg_link, token, len);
6572		smart_str_appends(querystr, escaped);
6573		PGSQLfree(escaped);
6574	}
6575	if (tmp && *tmp) {
6576		len = strlen(tmp);
6577		/* "schema"."table" format */
6578		if (_php_pgsql_detect_identifier_escape(tmp, len) == SUCCESS) {
6579			smart_str_appendc(querystr, '.');
6580			smart_str_appendl(querystr, tmp, len);
6581		} else {
6582			escaped = PGSQLescapeIdentifier(pg_link, tmp, len);
6583			smart_str_appendc(querystr, '.');
6584			smart_str_appends(querystr, escaped);
6585			PGSQLfree(escaped);
6586		}
6587	}
6588	efree(table_copy);
6589}
6590/* }}} */
6591
6592/* {{{ php_pgsql_insert
6593 */
6594PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, zend_ulong opt, zend_string **sql)
6595{
6596	zval *val, converted;
6597	char buf[256];
6598	char *tmp;
6599	smart_str querystr = {0};
6600	int ret = FAILURE;
6601	zend_string *fld;
6602
6603	assert(pg_link != NULL);
6604	assert(table != NULL);
6605	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6606
6607	ZVAL_UNDEF(&converted);
6608	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
6609		smart_str_appends(&querystr, "INSERT INTO ");
6610		build_tablename(&querystr, pg_link, table);
6611		smart_str_appends(&querystr, " DEFAULT VALUES");
6612
6613		goto no_values;
6614	}
6615
6616	/* convert input array if needed */
6617	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6618		array_init(&converted);
6619		if (php_pgsql_convert(pg_link, table, var_array, &converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6620			goto cleanup;
6621		}
6622		var_array = &converted;
6623	}
6624
6625	smart_str_appends(&querystr, "INSERT INTO ");
6626	build_tablename(&querystr, pg_link, table);
6627	smart_str_appends(&querystr, " (");
6628
6629	ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) {
6630		if (fld == NULL) {
6631			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6632			goto cleanup;
6633		}
6634		if (opt & PGSQL_DML_ESCAPE) {
6635			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6636			smart_str_appends(&querystr, tmp);
6637			PGSQLfree(tmp);
6638		} else {
6639			smart_str_appendl(&querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6640		}
6641		smart_str_appendc(&querystr, ',');
6642	} ZEND_HASH_FOREACH_END();
6643	ZSTR_LEN(querystr.s)--;
6644	smart_str_appends(&querystr, ") VALUES (");
6645
6646	/* make values string */
6647	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) {
6648		/* we can avoid the key_type check here, because we tested it in the other loop */
6649		switch (Z_TYPE_P(val)) {
6650			case IS_STRING:
6651				if (opt & PGSQL_DML_ESCAPE) {
6652					size_t new_len;
6653					char *tmp;
6654					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6655					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6656					smart_str_appendc(&querystr, '\'');
6657					smart_str_appendl(&querystr, tmp, new_len);
6658					smart_str_appendc(&querystr, '\'');
6659					efree(tmp);
6660				} else {
6661					smart_str_appendl(&querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6662				}
6663				break;
6664			case IS_LONG:
6665				smart_str_append_long(&querystr, Z_LVAL_P(val));
6666				break;
6667			case IS_DOUBLE:
6668				smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)));
6669				break;
6670			case IS_NULL:
6671				smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1);
6672				break;
6673			default:
6674				php_error_docref(NULL, E_WARNING, "Expects scaler values. type = %d", Z_TYPE_P(val));
6675				goto cleanup;
6676				break;
6677		}
6678		smart_str_appendc(&querystr, ',');
6679	} ZEND_HASH_FOREACH_END();
6680	/* Remove the trailing "," */
6681	ZSTR_LEN(querystr.s)--;
6682	smart_str_appends(&querystr, ");");
6683
6684no_values:
6685
6686	smart_str_0(&querystr);
6687
6688	if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
6689		do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS)) == 0) {
6690		ret = SUCCESS;
6691	}
6692	else if (opt & PGSQL_DML_STRING) {
6693		ret = SUCCESS;
6694	}
6695
6696cleanup:
6697	zval_ptr_dtor(&converted);
6698	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6699		*sql = querystr.s;
6700	}
6701	else {
6702		smart_str_free(&querystr);
6703	}
6704	return ret;
6705}
6706/* }}} */
6707
6708/* {{{ proto mixed pg_insert(resource db, string table, array values[, int options])
6709   Insert values (filed=>value) to table */
6710PHP_FUNCTION(pg_insert)
6711{
6712	zval *pgsql_link, *values;
6713	char *table;
6714	size_t table_len;
6715	zend_ulong option = PGSQL_DML_EXEC, return_sql;
6716	PGconn *pg_link;
6717	PGresult *pg_result;
6718	ExecStatusType status;
6719	pgsql_result_handle *pgsql_handle;
6720	zend_string *sql = NULL;
6721	int argc = ZEND_NUM_ARGS();
6722
6723	if (zend_parse_parameters(argc, "rsa|l",
6724							  &pgsql_link, &table, &table_len, &values, &option) == FAILURE) {
6725		return;
6726	}
6727	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6728		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6729		RETURN_FALSE;
6730	}
6731
6732	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6733		RETURN_FALSE;
6734	}
6735
6736	if (php_pgsql_flush_query(pg_link)) {
6737		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6738	}
6739	return_sql = option & PGSQL_DML_STRING;
6740	if (option & PGSQL_DML_EXEC) {
6741		/* return resource when executed */
6742		option = option & ~PGSQL_DML_EXEC;
6743		if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql) == FAILURE) {
6744			RETURN_FALSE;
6745		}
6746		pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6747		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) {
6748			PQclear(pg_result);
6749			PQreset(pg_link);
6750			pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6751		}
6752		efree(sql);
6753
6754		if (pg_result) {
6755			status = PQresultStatus(pg_result);
6756		} else {
6757			status = (ExecStatusType) PQstatus(pg_link);
6758		}
6759
6760		switch (status) {
6761			case PGRES_EMPTY_QUERY:
6762			case PGRES_BAD_RESPONSE:
6763			case PGRES_NONFATAL_ERROR:
6764			case PGRES_FATAL_ERROR:
6765				PHP_PQ_ERROR("Query failed: %s", pg_link);
6766				PQclear(pg_result);
6767				RETURN_FALSE;
6768				break;
6769			case PGRES_COMMAND_OK: /* successful command that did not return rows */
6770			default:
6771				if (pg_result) {
6772					pgsql_handle = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
6773					pgsql_handle->conn = pg_link;
6774					pgsql_handle->result = pg_result;
6775					pgsql_handle->row = 0;
6776					RETURN_RES(zend_register_resource(pgsql_handle, le_result));
6777				} else {
6778					PQclear(pg_result);
6779					RETURN_FALSE;
6780				}
6781			break;
6782		}
6783	} else if (php_pgsql_insert(pg_link, table, values, option, &sql) == FAILURE) {
6784		RETURN_FALSE;
6785	}
6786	if (return_sql) {
6787		RETURN_STR(sql);
6788		return;
6789	}
6790	RETURN_TRUE;
6791}
6792/* }}} */
6793
6794static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len, zend_ulong opt) /* {{{ */
6795{
6796	char *tmp;
6797	char buf[256];
6798	zend_string *fld;
6799	zval *val;
6800
6801	ZEND_HASH_FOREACH_STR_KEY_VAL(ht, fld, val) {
6802		if (fld == NULL) {
6803			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6804			return -1;
6805		}
6806		if (opt & PGSQL_DML_ESCAPE) {
6807			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6808			smart_str_appends(querystr, tmp);
6809			PGSQLfree(tmp);
6810		} else {
6811			smart_str_appendl(querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6812		}
6813		if (where_cond && (Z_TYPE_P(val) == IS_TRUE || Z_TYPE_P(val) == IS_FALSE || (Z_TYPE_P(val) == IS_STRING && !strcmp(Z_STRVAL_P(val), "NULL")))) {
6814			smart_str_appends(querystr, " IS ");
6815		} else {
6816			smart_str_appendc(querystr, '=');
6817		}
6818
6819		switch (Z_TYPE_P(val)) {
6820			case IS_STRING:
6821				if (opt & PGSQL_DML_ESCAPE) {
6822					size_t new_len;
6823					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6824					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6825					smart_str_appendc(querystr, '\'');
6826					smart_str_appendl(querystr, tmp, new_len);
6827					smart_str_appendc(querystr, '\'');
6828					efree(tmp);
6829				} else {
6830					smart_str_appendl(querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6831				}
6832				break;
6833			case IS_LONG:
6834				smart_str_append_long(querystr, Z_LVAL_P(val));
6835				break;
6836			case IS_DOUBLE:
6837				smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)), sizeof(buf)-1));
6838				break;
6839			case IS_NULL:
6840				smart_str_appendl(querystr, "NULL", sizeof("NULL")-1);
6841				break;
6842			default:
6843				php_error_docref(NULL, E_WARNING, "Expects scaler values. type=%d", Z_TYPE_P(val));
6844				return -1;
6845		}
6846		smart_str_appendl(querystr, pad, pad_len);
6847	} ZEND_HASH_FOREACH_END();
6848	if (querystr->s) {
6849		ZSTR_LEN(querystr->s) -= pad_len;
6850	}
6851
6852	return 0;
6853}
6854/* }}} */
6855
6856/* {{{ php_pgsql_update
6857 */
6858PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var_array, zval *ids_array, zend_ulong opt, zend_string **sql)
6859{
6860	zval var_converted, ids_converted;
6861	smart_str querystr = {0};
6862	int ret = FAILURE;
6863
6864	assert(pg_link != NULL);
6865	assert(table != NULL);
6866	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6867	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6868	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6869
6870	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
6871			|| zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6872		return FAILURE;
6873	}
6874
6875	ZVAL_UNDEF(&var_converted);
6876	ZVAL_UNDEF(&ids_converted);
6877	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6878		array_init(&var_converted);
6879		if (php_pgsql_convert(pg_link, table, var_array, &var_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6880			goto cleanup;
6881		}
6882		var_array = &var_converted;
6883		array_init(&ids_converted);
6884		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6885			goto cleanup;
6886		}
6887		ids_array = &ids_converted;
6888	}
6889
6890	smart_str_appends(&querystr, "UPDATE ");
6891	build_tablename(&querystr, pg_link, table);
6892	smart_str_appends(&querystr, " SET ");
6893
6894	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt))
6895		goto cleanup;
6896
6897	smart_str_appends(&querystr, " WHERE ");
6898
6899	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6900		goto cleanup;
6901
6902	smart_str_appendc(&querystr, ';');
6903	smart_str_0(&querystr);
6904
6905	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6906		ret = SUCCESS;
6907	} else if (opt & PGSQL_DML_STRING) {
6908		ret = SUCCESS;
6909	}
6910
6911cleanup:
6912	zval_ptr_dtor(&var_converted);
6913	zval_ptr_dtor(&ids_converted);
6914	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6915		*sql = querystr.s;
6916	}
6917	else {
6918		smart_str_free(&querystr);
6919	}
6920	return ret;
6921}
6922/* }}} */
6923
6924/* {{{ proto mixed pg_update(resource db, string table, array fields, array ids[, int options])
6925   Update table using values (field=>value) and ids (id=>value) */
6926PHP_FUNCTION(pg_update)
6927{
6928	zval *pgsql_link, *values, *ids;
6929	char *table;
6930	size_t table_len;
6931	zend_ulong option =  PGSQL_DML_EXEC;
6932	PGconn *pg_link;
6933	zend_string *sql = NULL;
6934	int argc = ZEND_NUM_ARGS();
6935
6936	if (zend_parse_parameters(argc, "rsaa|l",
6937							  &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) {
6938		return;
6939	}
6940	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6941		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6942		RETURN_FALSE;
6943	}
6944
6945	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6946		RETURN_FALSE;
6947	}
6948
6949	if (php_pgsql_flush_query(pg_link)) {
6950		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6951	}
6952	if (php_pgsql_update(pg_link, table, values, ids, option, &sql) == FAILURE) {
6953		RETURN_FALSE;
6954	}
6955	if (option & PGSQL_DML_STRING) {
6956		RETURN_STR(sql);
6957	}
6958	RETURN_TRUE;
6959}
6960/* }}} */
6961
6962/* {{{ php_pgsql_delete
6963 */
6964PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids_array, zend_ulong opt, zend_string **sql)
6965{
6966	zval ids_converted;
6967	smart_str querystr = {0};
6968	int ret = FAILURE;
6969
6970	assert(pg_link != NULL);
6971	assert(table != NULL);
6972	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6973	assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6974
6975	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6976		return FAILURE;
6977	}
6978
6979	ZVAL_UNDEF(&ids_converted);
6980	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6981		array_init(&ids_converted);
6982		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6983			goto cleanup;
6984		}
6985		ids_array = &ids_converted;
6986	}
6987
6988	smart_str_appends(&querystr, "DELETE FROM ");
6989	build_tablename(&querystr, pg_link, table);
6990	smart_str_appends(&querystr, " WHERE ");
6991
6992	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6993		goto cleanup;
6994
6995	smart_str_appendc(&querystr, ';');
6996	smart_str_0(&querystr);
6997
6998	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6999		ret = SUCCESS;
7000	} else if (opt & PGSQL_DML_STRING) {
7001		ret = SUCCESS;
7002	}
7003
7004cleanup:
7005	zval_ptr_dtor(&ids_converted);
7006	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7007		*sql = querystr.s;
7008	}
7009	else {
7010		smart_str_free(&querystr);
7011	}
7012	return ret;
7013}
7014/* }}} */
7015
7016/* {{{ proto mixed pg_delete(resource db, string table, array ids[, int options])
7017   Delete records has ids (id=>value) */
7018PHP_FUNCTION(pg_delete)
7019{
7020	zval *pgsql_link, *ids;
7021	char *table;
7022	size_t table_len;
7023	zend_ulong option = PGSQL_DML_EXEC;
7024	PGconn *pg_link;
7025	zend_string *sql;
7026	int argc = ZEND_NUM_ARGS();
7027
7028	if (zend_parse_parameters(argc, "rsa|l",
7029							  &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
7030		return;
7031	}
7032	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7033		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7034		RETURN_FALSE;
7035	}
7036
7037	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7038		RETURN_FALSE;
7039	}
7040
7041	if (php_pgsql_flush_query(pg_link)) {
7042		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7043	}
7044	if (php_pgsql_delete(pg_link, table, ids, option, &sql) == FAILURE) {
7045		RETURN_FALSE;
7046	}
7047	if (option & PGSQL_DML_STRING) {
7048		RETURN_STR(sql);
7049	}
7050	RETURN_TRUE;
7051}
7052/* }}} */
7053
7054/* {{{ php_pgsql_result2array
7055 */
7056PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
7057{
7058	zval row;
7059	char *field_name;
7060	size_t num_fields;
7061	int pg_numrows, pg_row;
7062	uint i;
7063	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7064
7065	if ((pg_numrows = PQntuples(pg_result)) <= 0) {
7066		return FAILURE;
7067	}
7068	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
7069		array_init(&row);
7070		for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
7071			field_name = PQfname(pg_result, i);
7072			if (PQgetisnull(pg_result, pg_row, i)) {
7073				if (result_type & PGSQL_ASSOC) {
7074					add_assoc_null(&row, field_name);
7075				}
7076				if (result_type & PGSQL_NUM) {
7077					add_next_index_null(&row);
7078				}
7079			} else {
7080				char *element = PQgetvalue(pg_result, pg_row, i);
7081				if (element) {
7082					const size_t element_len = strlen(element);
7083					if (result_type & PGSQL_ASSOC) {
7084						add_assoc_stringl(&row, field_name, element, element_len);
7085					}
7086					if (result_type & PGSQL_NUM) {
7087						add_next_index_stringl(&row, element, element_len);
7088					}
7089				}
7090			}
7091		}
7092		add_index_zval(ret_array, pg_row, &row);
7093	}
7094	return SUCCESS;
7095}
7096/* }}} */
7097
7098/* {{{ php_pgsql_select
7099 */
7100 PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, long result_type, zend_string **sql)
7101{
7102	zval ids_converted;
7103	smart_str querystr = {0};
7104	int ret = FAILURE;
7105	PGresult *pg_result;
7106
7107	assert(pg_link != NULL);
7108	assert(table != NULL);
7109	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
7110	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7111	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
7112
7113	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
7114		return FAILURE;
7115	}
7116
7117	ZVAL_UNDEF(&ids_converted);
7118	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
7119		array_init(&ids_converted);
7120		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
7121			goto cleanup;
7122		}
7123		ids_array = &ids_converted;
7124	}
7125
7126	smart_str_appends(&querystr, "SELECT * FROM ");
7127	build_tablename(&querystr, pg_link, table);
7128	smart_str_appends(&querystr, " WHERE ");
7129
7130	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
7131		goto cleanup;
7132
7133	smart_str_appendc(&querystr, ';');
7134	smart_str_0(&querystr);
7135
7136	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
7137	if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
7138		ret = php_pgsql_result2array(pg_result, ret_array, result_type);
7139	} else {
7140		php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
7141	}
7142	PQclear(pg_result);
7143
7144cleanup:
7145	zval_ptr_dtor(&ids_converted);
7146	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7147		*sql = querystr.s;
7148	}
7149	else {
7150		smart_str_free(&querystr);
7151	}
7152	return ret;
7153}
7154/* }}} */
7155
7156/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
7157   Select records that has ids (id=>value) */
7158PHP_FUNCTION(pg_select)
7159{
7160	zval *pgsql_link, *ids;
7161	char *table;
7162	size_t table_len;
7163	zend_ulong option = PGSQL_DML_EXEC;
7164	long result_type = PGSQL_ASSOC;
7165	PGconn *pg_link;
7166	zend_string *sql = NULL;
7167	int argc = ZEND_NUM_ARGS();
7168
7169	if (zend_parse_parameters(argc, "rsa|l",
7170							  &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
7171		return;
7172	}
7173	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7174		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7175		RETURN_FALSE;
7176	}
7177	if (!(result_type & PGSQL_BOTH)) {
7178		php_error_docref(NULL, E_WARNING, "Invalid result type");
7179		RETURN_FALSE;
7180	}
7181
7182	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7183		RETURN_FALSE;
7184	}
7185
7186	if (php_pgsql_flush_query(pg_link)) {
7187		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7188	}
7189	array_init(return_value);
7190	if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
7191		zval_ptr_dtor(return_value);
7192		RETURN_FALSE;
7193	}
7194	if (option & PGSQL_DML_STRING) {
7195		zval_ptr_dtor(return_value);
7196		RETURN_STR(sql);
7197	}
7198	return;
7199}
7200/* }}} */
7201
7202#endif
7203
7204/*
7205 * Local variables:
7206 * tab-width: 4
7207 * c-basic-offset: 4
7208 * End:
7209 * vim600: sw=4 ts=4 fdm=marker
7210 * vim<600: sw=4 ts=4
7211 */
7212