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