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