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