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