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