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