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