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