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