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