1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Zeev Suraski <zeev@zend.com>                                |
16   |          Jouni Ahto <jouni.ahto@exdec.fi>                            |
17   |          Yasuo Ohgaki <yohgaki@php.net>                              |
18   |          Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*)         |
19   |          Chris Kings-Lynne <chriskl@php.net> (v3 protocol)           |
20   +----------------------------------------------------------------------+
21 */
22
23/* $Id$ */
24
25#include <stdlib.h>
26
27#define PHP_PGSQL_PRIVATE 1
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#define SMART_STR_PREALLOC 512
34
35#include "php.h"
36#include "php_ini.h"
37#include "ext/standard/php_standard.h"
38#include "ext/standard/php_smart_str.h"
39#include "ext/ereg/php_regex.h"
40#ifdef PHP_WIN32
41# include "win32/time.h"
42#endif
43
44#undef PACKAGE_BUGREPORT
45#undef PACKAGE_NAME
46#undef PACKAGE_STRING
47#undef PACKAGE_TARNAME
48#undef PACKAGE_VERSION
49#include "php_pgsql.h"
50#include "php_globals.h"
51#include "zend_exceptions.h"
52
53#if HAVE_PGSQL
54
55#ifndef InvalidOid
56#define InvalidOid ((Oid) 0)
57#endif
58
59#define PGSQL_ASSOC     1<<0
60#define PGSQL_NUM       1<<1
61#define PGSQL_BOTH      (PGSQL_ASSOC|PGSQL_NUM)
62
63#define PGSQL_STATUS_LONG     1
64#define PGSQL_STATUS_STRING   2
65
66#define PGSQL_MAX_LENGTH_OF_LONG   30
67#define PGSQL_MAX_LENGTH_OF_DOUBLE 60
68
69#if LONG_MAX < UINT_MAX
70#define PGSQL_RETURN_OID(oid) do { \
71    if (oid > LONG_MAX) { \
72        smart_str s = {0}; \
73        smart_str_append_unsigned(&s, oid); \
74        smart_str_0(&s); \
75        RETURN_STRINGL(s.c, s.len, 0); \
76    } \
77    RETURN_LONG((long)oid); \
78} while(0)
79#else
80#define PGSQL_RETURN_OID(oid) (RETURN_LONG((long)oid))
81#endif
82
83#if HAVE_PQSETNONBLOCKING
84#define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
85#else
86#define PQ_SETNONBLOCKING(pg_link, flag) 0
87#endif
88
89#define CHECK_DEFAULT_LINK(x) if ((x) == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "No PostgreSQL link opened yet"); }
90
91#ifndef HAVE_PQFREEMEM
92#define PQfreemem free
93#endif
94
95ZEND_DECLARE_MODULE_GLOBALS(pgsql)
96static PHP_GINIT_FUNCTION(pgsql);
97
98/* {{{ arginfo */
99ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
100    ZEND_ARG_INFO(0, connection_string)
101    ZEND_ARG_INFO(0, connect_type)
102    ZEND_ARG_INFO(0, host)
103    ZEND_ARG_INFO(0, port)
104    ZEND_ARG_INFO(0, options)
105    ZEND_ARG_INFO(0, tty)
106    ZEND_ARG_INFO(0, database)
107ZEND_END_ARG_INFO()
108
109ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
110    ZEND_ARG_INFO(0, connection_string)
111    ZEND_ARG_INFO(0, host)
112    ZEND_ARG_INFO(0, port)
113    ZEND_ARG_INFO(0, options)
114    ZEND_ARG_INFO(0, tty)
115    ZEND_ARG_INFO(0, database)
116ZEND_END_ARG_INFO()
117
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_string(&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)) {
3635        RETURN_TRUE;
3636    }
3637    RETURN_FALSE;
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                    convert_to_string_ex(tmp);
4063                    query = (char *)emalloc(Z_STRLEN_PP(tmp) + 2);
4064                    strlcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp) + 2);
4065                    if(Z_STRLEN_PP(tmp) > 0 && *(query + Z_STRLEN_PP(tmp) - 1) != '\n') {
4066                        strlcat(query, "\n", Z_STRLEN_PP(tmp) + 2);
4067                    }
4068                    if (PQputCopyData(pgsql, query, strlen(query)) != 1) {
4069                        efree(query);
4070                        PHP_PQ_ERROR("copy failed: %s", pgsql);
4071                        RETURN_FALSE;
4072                    }
4073                    efree(query);
4074                    zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
4075                }
4076                if (PQputCopyEnd(pgsql, NULL) != 1) {
4077                    PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
4078                    RETURN_FALSE;
4079                }
4080#else
4081                while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
4082                    convert_to_string_ex(tmp);
4083                    query = (char *)emalloc(Z_STRLEN_PP(tmp) + 2);
4084                    strlcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp) + 2);
4085                    if(Z_STRLEN_PP(tmp) > 0 && *(query + Z_STRLEN_PP(tmp) - 1) != '\n') {
4086                        strlcat(query, "\n", Z_STRLEN_PP(tmp) + 2);
4087                    }
4088                    if (PQputline(pgsql, query)==EOF) {
4089                        efree(query);
4090                        PHP_PQ_ERROR("copy failed: %s", pgsql);
4091                        RETURN_FALSE;
4092                    }
4093                    efree(query);
4094                    zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
4095                }
4096                if (PQputline(pgsql, "\\.\n") == EOF) {
4097                    PHP_PQ_ERROR("putline failed: %s", pgsql);
4098                    RETURN_FALSE;
4099                }
4100                if (PQendcopy(pgsql)) {
4101                    PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4102                    RETURN_FALSE;
4103                }
4104#endif
4105                while ((pgsql_result = PQgetResult(pgsql))) {
4106                    if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
4107                        PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4108                        command_failed = 1;
4109                    }
4110                    PQclear(pgsql_result);
4111                }
4112                if (command_failed) {
4113                    RETURN_FALSE;
4114                }
4115            } else {
4116                PQclear(pgsql_result);
4117                RETURN_FALSE;
4118            }
4119            RETURN_TRUE;
4120            break;
4121        default:
4122            PQclear(pgsql_result);
4123            PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4124            RETURN_FALSE;
4125            break;
4126    }
4127}
4128/* }}} */
4129
4130#ifdef HAVE_PQESCAPE
4131/* {{{ proto string pg_escape_string([resource connection,] string data)
4132   Escape string for text/char type */
4133PHP_FUNCTION(pg_escape_string)
4134{
4135    char *from = NULL, *to = NULL;
4136    zval *pgsql_link;
4137#ifdef HAVE_PQESCAPE_CONN
4138    PGconn *pgsql;
4139#endif
4140    int to_len;
4141    int from_len;
4142    int id = -1;
4143
4144    switch (ZEND_NUM_ARGS()) {
4145        case 1:
4146            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
4147                return;
4148            }
4149            pgsql_link = NULL;
4150            id = PGG(default_link);
4151            break;
4152
4153        default:
4154            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4155                return;
4156            }
4157            break;
4158    }
4159
4160    to = (char *) safe_emalloc(from_len, 2, 1);
4161#ifdef HAVE_PQESCAPE_CONN
4162    if (pgsql_link != NULL || id != -1) {
4163        ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4164        to_len = (int) PQescapeStringConn(pgsql, to, from, (size_t)from_len, NULL);
4165    } else
4166#endif
4167        to_len = (int) PQescapeString(to, from, (size_t)from_len);
4168
4169    RETURN_STRINGL(to, to_len, 0);
4170}
4171/* }}} */
4172
4173/* {{{ proto string pg_escape_bytea([resource connection,] string data)
4174   Escape binary for bytea type  */
4175PHP_FUNCTION(pg_escape_bytea)
4176{
4177    char *from = NULL, *to = NULL;
4178    size_t to_len;
4179    int from_len, id = -1;
4180#ifdef HAVE_PQESCAPE_BYTEA_CONN
4181    PGconn *pgsql;
4182#endif
4183    zval *pgsql_link;
4184
4185    switch (ZEND_NUM_ARGS()) {
4186        case 1:
4187            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
4188                return;
4189            }
4190            pgsql_link = NULL;
4191            id = PGG(default_link);
4192            break;
4193
4194        default:
4195            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4196                return;
4197            }
4198            break;
4199    }
4200
4201#ifdef HAVE_PQESCAPE_BYTEA_CONN
4202    if (pgsql_link != NULL || id != -1) {
4203        ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4204        to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)from, (size_t)from_len, &to_len);
4205    } else
4206#endif
4207        to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
4208
4209    RETVAL_STRINGL(to, to_len-1, 1); /* to_len includes addtional '\0' */
4210    PQfreemem(to);
4211}
4212/* }}} */
4213
4214#if !HAVE_PQUNESCAPEBYTEA
4215/* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
4216   Renamed to php_pgsql_unescape_bytea() */
4217/*
4218 *      PQunescapeBytea - converts the null terminated string representation
4219 *      of a bytea, strtext, into binary, filling a buffer. It returns a
4220 *      pointer to the buffer which is NULL on error, and the size of the
4221 *      buffer in retbuflen. The pointer may subsequently be used as an
4222 *      argument to the function free(3). It is the reverse of PQescapeBytea.
4223 *
4224 *      The following transformations are reversed:
4225 *      '\0' == ASCII  0 == \000
4226 *      '\'' == ASCII 39 == \'
4227 *      '\\' == ASCII 92 == \\
4228 *
4229 *      States:
4230 *      0   normal      0->1->2->3->4
4231 *      1   \              1->5
4232 *      2   \0             1->6
4233 *      3   \00
4234 *      4   \000
4235 *      5   \'
4236 *      6   \\
4237 */
4238static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen)
4239{
4240    size_t     buflen;
4241    unsigned char *buffer,
4242               *sp,
4243               *bp;
4244    unsigned int state = 0;
4245
4246    if (strtext == NULL)
4247        return NULL;
4248    buflen = strlen(strtext);   /* will shrink, also we discover if
4249                                 * strtext */
4250    buffer = (unsigned char *) emalloc(buflen); /* isn't NULL terminated */
4251    for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
4252    {
4253        switch (state)
4254        {
4255            case 0:
4256                if (*sp == '\\')
4257                    state = 1;
4258                *bp = *sp;
4259                break;
4260            case 1:
4261                if (*sp == '\'')    /* state=5 */
4262                {               /* replace \' with 39 */
4263                    bp--;
4264                    *bp = '\'';
4265                    buflen--;
4266                    state = 0;
4267                }
4268                else if (*sp == '\\')   /* state=6 */
4269                {               /* replace \\ with 92 */
4270                    bp--;
4271                    *bp = '\\';
4272                    buflen--;
4273                    state = 0;
4274                }
4275                else
4276                {
4277                    if (isdigit(*sp))
4278                        state = 2;
4279                    else
4280                        state = 0;
4281                    *bp = *sp;
4282                }
4283                break;
4284            case 2:
4285                if (isdigit(*sp))
4286                    state = 3;
4287                else
4288                    state = 0;
4289                *bp = *sp;
4290                break;
4291            case 3:
4292                if (isdigit(*sp))       /* state=4 */
4293                {
4294                    unsigned char *start, *end, buf[4]; /* 000 + '\0' */
4295
4296                    bp -= 3;
4297                    memcpy(buf, sp-2, 3);
4298                    buf[3] = '\0';
4299                    start = buf;
4300                    *bp = (unsigned char)strtoul(start, (char **)&end, 8);
4301                    buflen -= 3;
4302                    state = 0;
4303                }
4304                else
4305                {
4306                    *bp = *sp;
4307                    state = 0;
4308                }
4309                break;
4310        }
4311    }
4312    buffer = erealloc(buffer, buflen+1);
4313    buffer[buflen] = '\0';
4314
4315    *retbuflen = buflen;
4316    return buffer;
4317}
4318#endif
4319
4320/* {{{ proto string pg_unescape_bytea(string data)
4321   Unescape binary for bytea type  */
4322PHP_FUNCTION(pg_unescape_bytea)
4323{
4324    char *from = NULL, *to = NULL, *tmp = NULL;
4325    size_t to_len;
4326    int from_len;
4327    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
4328                              &from, &from_len) == FAILURE) {
4329        return;
4330    }
4331
4332#if HAVE_PQUNESCAPEBYTEA
4333    tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
4334    to = estrndup(tmp, to_len);
4335    PQfreemem(tmp);
4336#else
4337    to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
4338#endif
4339    if (!to) {
4340        RETURN_FALSE;
4341    }
4342    RETVAL_STRINGL(to, to_len, 0);
4343}
4344/* }}} */
4345#endif
4346
4347#ifdef HAVE_PQESCAPE
4348static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) {
4349    char *from = NULL, *to = NULL;
4350    zval *pgsql_link = NULL;
4351    PGconn *pgsql;
4352    int from_len;
4353    int id = -1;
4354    char *tmp;
4355
4356    switch (ZEND_NUM_ARGS()) {
4357        case 1:
4358            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
4359                return;
4360            }
4361            pgsql_link = NULL;
4362            id = PGG(default_link);
4363            break;
4364
4365        default:
4366            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4367                return;
4368            }
4369            break;
4370    }
4371
4372    if (pgsql_link == NULL && id == -1) {
4373        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot get default pgsql link");
4374        RETURN_FALSE;
4375    }
4376
4377    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4378    if (pgsql == NULL) {
4379        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot get pgsql link");
4380        RETURN_FALSE;
4381    }
4382
4383    if (escape_literal) {
4384        tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
4385    } else {
4386        tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
4387    }
4388    if (!tmp) {
4389        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape");
4390        RETURN_FALSE;
4391    }
4392    to = estrdup(tmp);
4393    PGSQLfree(tmp);
4394
4395    RETURN_STRING(to, 0);
4396}
4397
4398/* {{{ proto string pg_escape_literal([resource connection,] string data)
4399   Escape parameter as string literal (i.e. parameter)  */
4400PHP_FUNCTION(pg_escape_literal)
4401{
4402    php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4403}
4404/* }}} */
4405
4406/* {{{ proto string pg_escape_identifier([resource connection,] string data)
4407   Escape identifier (i.e. table name, field name)  */
4408PHP_FUNCTION(pg_escape_identifier)
4409{
4410    php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4411}
4412/* }}} */
4413#endif
4414
4415
4416/* {{{ proto string pg_result_error(resource result)
4417   Get error message associated with result */
4418PHP_FUNCTION(pg_result_error)
4419{
4420    zval *result;
4421    PGresult *pgsql_result;
4422    pgsql_result_handle *pg_result;
4423    char *err = NULL;
4424
4425    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
4426                                 &result) == FAILURE) {
4427        RETURN_FALSE;
4428    }
4429
4430    ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
4431
4432    pgsql_result = pg_result->result;
4433    if (!pgsql_result) {
4434        RETURN_FALSE;
4435    }
4436    err = (char *)PQresultErrorMessage(pgsql_result);
4437    RETURN_STRING(err,1);
4438}
4439/* }}} */
4440
4441
4442#if HAVE_PQRESULTERRORFIELD
4443/* {{{ proto string pg_result_error_field(resource result, int fieldcode)
4444   Get error message field associated with result */
4445PHP_FUNCTION(pg_result_error_field)
4446{
4447    zval *result;
4448    long fieldcode;
4449    PGresult *pgsql_result;
4450    pgsql_result_handle *pg_result;
4451    char *field = NULL;
4452
4453    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "rl",
4454                                 &result, &fieldcode) == FAILURE) {
4455        RETURN_FALSE;
4456    }
4457
4458    ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
4459
4460    pgsql_result = pg_result->result;
4461    if (!pgsql_result) {
4462        RETURN_FALSE;
4463    }
4464    if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
4465                |PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
4466#if PG_DIAG_INTERNAL_POSITION
4467                |PG_DIAG_INTERNAL_POSITION
4468#endif
4469#if PG_DIAG_INTERNAL_QUERY
4470                |PG_DIAG_INTERNAL_QUERY
4471#endif
4472                |PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
4473                |PG_DIAG_SOURCE_FUNCTION)) {
4474        field = (char *)PQresultErrorField(pgsql_result, fieldcode);
4475        if (field == NULL) {
4476            RETURN_NULL();
4477        } else {
4478            RETURN_STRING(field, 1);
4479        }
4480    } else {
4481        RETURN_FALSE;
4482    }
4483}
4484/* }}} */
4485#endif
4486
4487
4488/* {{{ proto int pg_connection_status(resource connection)
4489   Get connection status */
4490PHP_FUNCTION(pg_connection_status)
4491{
4492    zval *pgsql_link = NULL;
4493    int id = -1;
4494    PGconn *pgsql;
4495
4496    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
4497                                 &pgsql_link) == FAILURE) {
4498        RETURN_FALSE;
4499    }
4500
4501    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4502
4503    RETURN_LONG(PQstatus(pgsql));
4504}
4505
4506/* }}} */
4507
4508
4509#if HAVE_PGTRANSACTIONSTATUS
4510/* {{{ proto int pg_transaction_status(resource connection)
4511   Get transaction status */
4512PHP_FUNCTION(pg_transaction_status)
4513{
4514    zval *pgsql_link = NULL;
4515    int id = -1;
4516    PGconn *pgsql;
4517
4518    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
4519                                 &pgsql_link) == FAILURE) {
4520        RETURN_FALSE;
4521    }
4522
4523    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4524
4525    RETURN_LONG(PQtransactionStatus(pgsql));
4526}
4527#endif
4528
4529/* }}} */
4530
4531
4532/* {{{ proto bool pg_connection_reset(resource connection)
4533   Reset connection (reconnect) */
4534PHP_FUNCTION(pg_connection_reset)
4535{
4536    zval *pgsql_link;
4537    int id = -1;
4538    PGconn *pgsql;
4539
4540    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
4541                                 &pgsql_link) == FAILURE) {
4542        RETURN_FALSE;
4543    }
4544
4545    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4546
4547    PQreset(pgsql);
4548    if (PQstatus(pgsql) == CONNECTION_BAD) {
4549        RETURN_FALSE;
4550    }
4551    RETURN_TRUE;
4552}
4553/* }}} */
4554
4555
4556#define PHP_PG_ASYNC_IS_BUSY        1
4557#define PHP_PG_ASYNC_REQUEST_CANCEL 2
4558
4559
4560/* {{{ php_pgsql_flush_query
4561 */
4562static int php_pgsql_flush_query(PGconn *pgsql TSRMLS_DC)
4563{
4564    PGresult *res;
4565    int leftover = 0;
4566
4567    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4568        php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to nonblocking mode");
4569        return -1;
4570    }
4571    while ((res = PQgetResult(pgsql))) {
4572        PQclear(res);
4573        leftover++;
4574    }
4575    PQ_SETNONBLOCKING(pgsql, 0);
4576    return leftover;
4577}
4578/* }}} */
4579
4580
4581/* {{{ php_pgsql_do_async
4582 */
4583static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
4584{
4585    zval *pgsql_link;
4586    int id = -1;
4587    PGconn *pgsql;
4588    PGresult *pgsql_result;
4589
4590    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
4591                                 &pgsql_link) == FAILURE) {
4592        RETURN_FALSE;
4593    }
4594
4595    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4596
4597    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4598        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
4599        RETURN_FALSE;
4600    }
4601    switch(entry_type) {
4602        case PHP_PG_ASYNC_IS_BUSY:
4603            PQconsumeInput(pgsql);
4604            Z_LVAL_P(return_value) = PQisBusy(pgsql);
4605            Z_TYPE_P(return_value) = IS_LONG;
4606            break;
4607        case PHP_PG_ASYNC_REQUEST_CANCEL:
4608            Z_LVAL_P(return_value) = PQrequestCancel(pgsql);
4609            Z_TYPE_P(return_value) = IS_LONG;
4610            while ((pgsql_result = PQgetResult(pgsql))) {
4611                PQclear(pgsql_result);
4612            }
4613            break;
4614        default:
4615            php_error_docref(NULL TSRMLS_CC, E_ERROR, "PostgreSQL module error, please report this error");
4616            break;
4617    }
4618    if (PQ_SETNONBLOCKING(pgsql, 0)) {
4619        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
4620    }
4621    convert_to_boolean_ex(&return_value);
4622}
4623/* }}} */
4624
4625/* {{{ proto bool pg_cancel_query(resource connection)
4626   Cancel request */
4627PHP_FUNCTION(pg_cancel_query)
4628{
4629    php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
4630}
4631/* }}} */
4632
4633/* {{{ proto bool pg_connection_busy(resource connection)
4634   Get connection is busy or not */
4635PHP_FUNCTION(pg_connection_busy)
4636{
4637    php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
4638}
4639/* }}} */
4640
4641/* {{{ proto bool pg_send_query(resource connection, string query)
4642   Send asynchronous query */
4643PHP_FUNCTION(pg_send_query)
4644{
4645    zval *pgsql_link;
4646    char *query;
4647    int len;
4648    int id = -1;
4649    PGconn *pgsql;
4650    PGresult *res;
4651    int leftover = 0;
4652    int ret;
4653
4654    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
4655                              &pgsql_link, &query, &len) == FAILURE) {
4656        return;
4657    }
4658
4659    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4660
4661    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4662        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
4663        RETURN_FALSE;
4664    }
4665    while ((res = PQgetResult(pgsql))) {
4666        PQclear(res);
4667        leftover = 1;
4668    }
4669    if (leftover) {
4670        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
4671    }
4672    if (!PQsendQuery(pgsql, query)) {
4673        if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4674            PQreset(pgsql);
4675        }
4676        if (!PQsendQuery(pgsql, query)) {
4677            RETURN_FALSE;
4678        }
4679    }
4680    /* Wait to finish sending buffer */
4681    while ((ret = PQflush(pgsql))) {
4682        if (ret == -1) {
4683            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer");
4684            break;
4685        }
4686        usleep(10000);
4687    }
4688    if (PQ_SETNONBLOCKING(pgsql, 0)) {
4689        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
4690    }
4691    RETURN_TRUE;
4692}
4693/* }}} */
4694
4695#if HAVE_PQSENDQUERYPARAMS
4696/* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
4697   Send asynchronous parameterized query */
4698PHP_FUNCTION(pg_send_query_params)
4699{
4700    zval *pgsql_link, *pv_param_arr, **tmp;
4701    int num_params = 0;
4702    char **params = NULL;
4703    char *query;
4704    int query_len, id = -1;
4705    PGconn *pgsql;
4706    PGresult *res;
4707    int leftover = 0;
4708    int ret;
4709
4710    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa/", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
4711        return;
4712    }
4713
4714    if (pgsql_link == NULL && id == -1) {
4715        RETURN_FALSE;
4716    }
4717
4718    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4719
4720    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4721        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
4722        RETURN_FALSE;
4723    }
4724    while ((res = PQgetResult(pgsql))) {
4725        PQclear(res);
4726        leftover = 1;
4727    }
4728    if (leftover) {
4729        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
4730    }
4731
4732    zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
4733    num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
4734    if (num_params > 0) {
4735        int i = 0;
4736        params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
4737
4738        for(i = 0; i < num_params; i++) {
4739            if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
4740                php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
4741                _php_pgsql_free_params(params, num_params);
4742                RETURN_FALSE;
4743            }
4744
4745            if (Z_TYPE_PP(tmp) == IS_NULL) {
4746                params[i] = NULL;
4747            } else {
4748                zval tmp_val = **tmp;
4749                zval_copy_ctor(&tmp_val);
4750                convert_to_string(&tmp_val);
4751                if (Z_TYPE(tmp_val) != IS_STRING) {
4752                    php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
4753                    zval_dtor(&tmp_val);
4754                    _php_pgsql_free_params(params, num_params);
4755                    RETURN_FALSE;
4756                }
4757                params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
4758                zval_dtor(&tmp_val);
4759            }
4760
4761            zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
4762        }
4763    }
4764
4765    if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
4766        if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4767            PQreset(pgsql);
4768        }
4769        if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
4770            _php_pgsql_free_params(params, num_params);
4771            RETURN_FALSE;
4772        }
4773    }
4774    _php_pgsql_free_params(params, num_params);
4775    /* Wait to finish sending buffer */
4776    while ((ret = PQflush(pgsql))) {
4777        if (ret == -1) {
4778            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer");
4779            break;
4780        }
4781        usleep(10000);
4782    }
4783    if (PQ_SETNONBLOCKING(pgsql, 0)) {
4784        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
4785    }
4786    RETURN_TRUE;
4787}
4788/* }}} */
4789#endif
4790
4791#if HAVE_PQSENDPREPARE
4792/* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
4793   Asynchronously prepare a query for future execution */
4794PHP_FUNCTION(pg_send_prepare)
4795{
4796    zval *pgsql_link;
4797    char *query, *stmtname;
4798    int stmtname_len, query_len, id = -1;
4799    PGconn *pgsql;
4800    PGresult *res;
4801    int leftover = 0;
4802    int ret;
4803
4804    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
4805        return;
4806    }
4807
4808    if (pgsql_link == NULL && id == -1) {
4809        RETURN_FALSE;
4810    }
4811
4812    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4813
4814    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4815        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
4816        RETURN_FALSE;
4817    }
4818    while ((res = PQgetResult(pgsql))) {
4819        PQclear(res);
4820        leftover = 1;
4821    }
4822    if (leftover) {
4823        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
4824    }
4825    if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
4826        if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4827            PQreset(pgsql);
4828        }
4829        if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
4830            RETURN_FALSE;
4831        }
4832    }
4833    /* Wait to finish sending buffer */
4834    while ((ret = PQflush(pgsql))) {
4835        if (ret == -1) {
4836            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty postgres send buffer");
4837            break;
4838        }
4839        usleep(10000);
4840    }
4841    if (PQ_SETNONBLOCKING(pgsql, 0)) {
4842        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
4843    }
4844    RETURN_TRUE;
4845}
4846/* }}} */
4847#endif
4848
4849#if HAVE_PQSENDQUERYPREPARED
4850/* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
4851   Executes prevriously prepared stmtname asynchronously */
4852PHP_FUNCTION(pg_send_execute)
4853{
4854    zval *pgsql_link;
4855    zval *pv_param_arr, **tmp;
4856    int num_params = 0;
4857    char **params = NULL;
4858    char *stmtname;
4859    int stmtname_len, id = -1;
4860    PGconn *pgsql;
4861    PGresult *res;
4862    int leftover = 0;
4863    int ret;
4864
4865    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
4866        return;
4867    }
4868
4869    if (pgsql_link == NULL && id == -1) {
4870        RETURN_FALSE;
4871    }
4872
4873    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4874
4875    if (PQ_SETNONBLOCKING(pgsql, 1)) {
4876        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
4877        RETURN_FALSE;
4878    }
4879    while ((res = PQgetResult(pgsql))) {
4880        PQclear(res);
4881        leftover = 1;
4882    }
4883    if (leftover) {
4884        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
4885    }
4886
4887    zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
4888    num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
4889    if (num_params > 0) {
4890        int i = 0;
4891        params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
4892
4893        for(i = 0; i < num_params; i++) {
4894            if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
4895                php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
4896                _php_pgsql_free_params(params, num_params);
4897                RETURN_FALSE;
4898            }
4899
4900            if (Z_TYPE_PP(tmp) == IS_NULL) {
4901                params[i] = NULL;
4902            } else {
4903                zval tmp_val = **tmp;
4904                zval_copy_ctor(&tmp_val);
4905                convert_to_string(&tmp_val);
4906                if (Z_TYPE(tmp_val) != IS_STRING) {
4907                    php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
4908                    zval_dtor(&tmp_val);
4909                    _php_pgsql_free_params(params, num_params);
4910                    RETURN_FALSE;
4911                }
4912                params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
4913                zval_dtor(&tmp_val);
4914            }
4915
4916            zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
4917        }
4918    }
4919
4920    if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
4921        if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4922            PQreset(pgsql);
4923        }
4924        if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
4925            _php_pgsql_free_params(params, num_params);
4926            RETURN_FALSE;
4927        }
4928    }
4929    _php_pgsql_free_params(params, num_params);
4930    /* Wait to finish sending buffer */
4931    while ((ret = PQflush(pgsql))) {
4932        if (ret == -1) {
4933            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty postgres send buffer");
4934            break;
4935        }
4936        usleep(10000);
4937    }
4938    if (PQ_SETNONBLOCKING(pgsql, 0)) {
4939        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
4940    }
4941    RETURN_TRUE;
4942}
4943/* }}} */
4944#endif
4945
4946/* {{{ proto resource pg_get_result(resource connection)
4947   Get asynchronous query result */
4948PHP_FUNCTION(pg_get_result)
4949{
4950    zval *pgsql_link;
4951    int id = -1;
4952    PGconn *pgsql;
4953    PGresult *pgsql_result;
4954    pgsql_result_handle *pg_result;
4955
4956    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) {
4957        RETURN_FALSE;
4958    }
4959
4960    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
4961
4962    pgsql_result = PQgetResult(pgsql);
4963    if (!pgsql_result) {
4964        /* no result */
4965        RETURN_FALSE;
4966    }
4967    pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
4968    pg_result->conn = pgsql;
4969    pg_result->result = pgsql_result;
4970    pg_result->row = 0;
4971    ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
4972}
4973/* }}} */
4974
4975/* {{{ proto mixed pg_result_status(resource result[, long result_type])
4976   Get status of query result */
4977PHP_FUNCTION(pg_result_status)
4978{
4979    zval *result;
4980    long result_type = PGSQL_STATUS_LONG;
4981    ExecStatusType status;
4982    PGresult *pgsql_result;
4983    pgsql_result_handle *pg_result;
4984
4985    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
4986                                 &result, &result_type) == FAILURE) {
4987        RETURN_FALSE;
4988    }
4989
4990    ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
4991
4992    pgsql_result = pg_result->result;
4993    if (result_type == PGSQL_STATUS_LONG) {
4994        status = PQresultStatus(pgsql_result);
4995        RETURN_LONG((int)status);
4996    }
4997    else if (result_type == PGSQL_STATUS_STRING) {
4998        RETURN_STRING(PQcmdStatus(pgsql_result), 1);
4999    }
5000    else {
5001        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
5002        RETURN_FALSE;
5003    }
5004}
5005/* }}} */
5006
5007
5008/* {{{ proto array pg_get_notify([resource connection[, result_type]])
5009   Get asynchronous notification */
5010PHP_FUNCTION(pg_get_notify)
5011{
5012    zval *pgsql_link;
5013    int id = -1;
5014    long result_type = PGSQL_ASSOC;
5015    PGconn *pgsql;
5016    PGnotify *pgsql_notify;
5017
5018    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
5019                                 &pgsql_link, &result_type) == FAILURE) {
5020        RETURN_FALSE;
5021    }
5022
5023    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
5024
5025    if (!(result_type & PGSQL_BOTH)) {
5026        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid result type");
5027        RETURN_FALSE;
5028    }
5029
5030    PQconsumeInput(pgsql);
5031    pgsql_notify = PQnotifies(pgsql);
5032    if (!pgsql_notify) {
5033        /* no notify message */
5034        RETURN_FALSE;
5035    }
5036    array_init(return_value);
5037    if (result_type & PGSQL_NUM) {
5038        add_index_string(return_value, 0, pgsql_notify->relname, 1);
5039        add_index_long(return_value, 1, pgsql_notify->be_pid);
5040#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5041        if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5042#else
5043        if (atof(PG_VERSION) >= 9.0) {
5044#endif
5045#if HAVE_PQPARAMETERSTATUS
5046            add_index_string(return_value, 2, pgsql_notify->extra, 1);
5047#endif
5048        }
5049    }
5050    if (result_type & PGSQL_ASSOC) {
5051        add_assoc_string(return_value, "message", pgsql_notify->relname, 1);
5052        add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
5053#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5054        if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5055#else
5056        if (atof(PG_VERSION) >= 9.0) {
5057#endif
5058#if HAVE_PQPARAMETERSTATUS
5059            add_assoc_string(return_value, "payload", pgsql_notify->extra, 1);
5060#endif
5061        }
5062    }
5063    PQfreemem(pgsql_notify);
5064}
5065/* }}} */
5066
5067/* {{{ proto int pg_get_pid([resource connection)
5068   Get backend(server) pid */
5069PHP_FUNCTION(pg_get_pid)
5070{
5071    zval *pgsql_link;
5072    int id = -1;
5073    PGconn *pgsql;
5074
5075    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
5076                                 &pgsql_link) == FAILURE) {
5077        RETURN_FALSE;
5078    }
5079
5080    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
5081
5082    RETURN_LONG(PQbackendPID(pgsql));
5083}
5084/* }}} */
5085
5086/* {{{ php_pgsql_meta_data
5087 * TODO: Add meta_data cache for better performance
5088 */
5089PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta TSRMLS_DC)
5090{
5091    PGresult *pg_result;
5092    char *src, *tmp_name, *tmp_name2 = NULL;
5093    char *escaped;
5094    smart_str querystr = {0};
5095    size_t new_len;
5096    int i, num_rows;
5097    zval *elem;
5098
5099    if (!*table_name) {
5100        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The table name must be specified");
5101        return FAILURE;
5102    }
5103
5104    src = estrdup(table_name);
5105    tmp_name = php_strtok_r(src, ".", &tmp_name2);
5106
5107    if (!tmp_name2 || !*tmp_name2) {
5108        /* Default schema */
5109        tmp_name2 = tmp_name;
5110        tmp_name = "public";
5111    }
5112
5113    smart_str_appends(&querystr,
5114            "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype = 'e' "
5115            "FROM pg_class as c, pg_attribute a, pg_type t, pg_namespace n "
5116            "WHERE a.attnum > 0 AND a.attrelid = c.oid AND c.relname = '");
5117    escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1);
5118    new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL);
5119    if (new_len) {
5120        smart_str_appendl(&querystr, escaped, new_len);
5121    }
5122    efree(escaped);
5123
5124    smart_str_appends(&querystr, "' AND c.relnamespace = n.oid AND n.nspname = '");
5125    escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1);
5126    new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL);
5127    if (new_len) {
5128        smart_str_appendl(&querystr, escaped, new_len);
5129    }
5130    efree(escaped);
5131
5132    smart_str_appends(&querystr, "' AND a.atttypid = t.oid ORDER BY a.attnum;");
5133    smart_str_0(&querystr);
5134    efree(src);
5135
5136    pg_result = PQexec(pg_link, querystr.c);
5137    if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
5138        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Table '%s' doesn't exists", table_name);
5139        smart_str_free(&querystr);
5140        PQclear(pg_result);
5141        return FAILURE;
5142    }
5143    smart_str_free(&querystr);
5144
5145    for (i = 0; i < num_rows; i++) {
5146        char *name;
5147        MAKE_STD_ZVAL(elem);
5148        array_init(elem);
5149        add_assoc_long(elem, "num", atoi(PQgetvalue(pg_result,i,1)));
5150        add_assoc_string(elem, "type", PQgetvalue(pg_result,i,2), 1);
5151        add_assoc_long(elem, "len", atoi(PQgetvalue(pg_result,i,3)));
5152        if (!strcmp(PQgetvalue(pg_result,i,4), "t")) {
5153            add_assoc_bool(elem, "not null", 1);
5154        }
5155        else {
5156            add_assoc_bool(elem, "not null", 0);
5157        }
5158        if (!strcmp(PQgetvalue(pg_result,i,5), "t")) {
5159            add_assoc_bool(elem, "has default", 1);
5160        }
5161        else {
5162            add_assoc_bool(elem, "has default", 0);
5163        }
5164        add_assoc_long(elem, "array dims", atoi(PQgetvalue(pg_result,i,6)));
5165        if (!strcmp(PQgetvalue(pg_result,i,7), "t")) {
5166            add_assoc_bool(elem, "is enum", 1);
5167        }
5168        else {
5169            add_assoc_bool(elem, "is enum", 0);
5170        }
5171        name = PQgetvalue(pg_result,i,0);
5172        add_assoc_zval(meta, name, elem);
5173    }
5174    PQclear(pg_result);
5175
5176    return SUCCESS;
5177}
5178
5179/* }}} */
5180
5181
5182/* {{{ proto array pg_meta_data(resource db, string table)
5183   Get meta_data */
5184PHP_FUNCTION(pg_meta_data)
5185{
5186    zval *pgsql_link;
5187    char *table_name;
5188    uint table_name_len;
5189    PGconn *pgsql;
5190    int id = -1;
5191
5192    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
5193                              &pgsql_link, &table_name, &table_name_len) == FAILURE) {
5194        return;
5195    }
5196
5197    ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
5198
5199    array_init(return_value);
5200    if (php_pgsql_meta_data(pgsql, table_name, return_value TSRMLS_CC) == FAILURE) {
5201        zval_dtor(return_value); /* destroy array */
5202        RETURN_FALSE;
5203    }
5204    else {
5205        HashPosition pos;
5206        zval **val;
5207
5208        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(return_value), &pos);
5209            zend_hash_get_current_data_ex(Z_ARRVAL_P(return_value), (void **)&val, &pos) == SUCCESS;
5210            zend_hash_move_forward_ex(Z_ARRVAL_P(return_value), &pos)) {
5211            /* delete newly added entry, in order to keep BC */
5212            zend_hash_del_key_or_index(Z_ARRVAL_PP(val), "is enum", sizeof("is enum"), 0, HASH_DEL_KEY);
5213        }
5214    }
5215}
5216/* }}} */
5217
5218
5219/* {{{ php_pgsql_get_data_type
5220 */
5221static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
5222{
5223    /* This is stupid way to do. I'll fix it when I decied how to support
5224       user defined types. (Yasuo) */
5225
5226    /* boolean */
5227    if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean"))
5228        return PG_BOOL;
5229    /* object id */
5230    if (!strcmp(type_name, "oid"))
5231        return PG_OID;
5232    /* integer */
5233    if (!strcmp(type_name, "int2") || !strcmp(type_name, "smallint"))
5234        return PG_INT2;
5235    if (!strcmp(type_name, "int4") || !strcmp(type_name, "integer"))
5236        return PG_INT4;
5237    if (!strcmp(type_name, "int8") || !strcmp(type_name, "bigint"))
5238        return PG_INT8;
5239    /* real and other */
5240    if (!strcmp(type_name, "float4") || !strcmp(type_name, "real"))
5241        return PG_FLOAT4;
5242    if (!strcmp(type_name, "float8") || !strcmp(type_name, "double precision"))
5243        return PG_FLOAT8;
5244    if (!strcmp(type_name, "numeric"))
5245        return PG_NUMERIC;
5246    if (!strcmp(type_name, "money"))
5247        return PG_MONEY;
5248    /* character */
5249    if (!strcmp(type_name, "text"))
5250        return PG_TEXT;
5251    if (!strcmp(type_name, "bpchar") || !strcmp(type_name, "character"))
5252        return PG_CHAR;
5253    if (!strcmp(type_name, "varchar") || !strcmp(type_name, "character varying"))
5254        return PG_VARCHAR;
5255    /* time and interval */
5256    if (!strcmp(type_name, "abstime"))
5257        return PG_UNIX_TIME;
5258    if (!strcmp(type_name, "reltime"))
5259        return PG_UNIX_TIME_INTERVAL;
5260    if (!strcmp(type_name, "tinterval"))
5261        return PG_UNIX_TIME_INTERVAL;
5262    if (!strcmp(type_name, "date"))
5263        return PG_DATE;
5264    if (!strcmp(type_name, "time"))
5265        return PG_TIME;
5266    if (!strcmp(type_name, "time with time zone") || !strcmp(type_name, "timetz"))
5267        return PG_TIME_WITH_TIMEZONE;
5268    if (!strcmp(type_name, "timestamp without time zone") || !strcmp(type_name, "timestamp"))
5269        return PG_TIMESTAMP;
5270    if (!strcmp(type_name, "timestamp with time zone") || !strcmp(type_name, "timestamptz"))
5271        return PG_TIMESTAMP_WITH_TIMEZONE;
5272    if (!strcmp(type_name, "interval"))
5273        return PG_INTERVAL;
5274    /* binary */
5275    if (!strcmp(type_name, "bytea"))
5276        return PG_BYTEA;
5277    /* network */
5278    if (!strcmp(type_name, "cidr"))
5279        return PG_CIDR;
5280    if (!strcmp(type_name, "inet"))
5281        return PG_INET;
5282    if (!strcmp(type_name, "macaddr"))
5283        return PG_MACADDR;
5284    /* bit */
5285    if (!strcmp(type_name, "bit"))
5286        return PG_BIT;
5287    if (!strcmp(type_name, "bit varying"))
5288        return PG_VARBIT;
5289    /* geometric */
5290    if (!strcmp(type_name, "line"))
5291        return PG_LINE;
5292    if (!strcmp(type_name, "lseg"))
5293        return PG_LSEG;
5294    if (!strcmp(type_name, "box"))
5295        return PG_BOX;
5296    if (!strcmp(type_name, "path"))
5297        return PG_PATH;
5298    if (!strcmp(type_name, "point"))
5299        return PG_POINT;
5300    if (!strcmp(type_name, "polygon"))
5301        return PG_POLYGON;
5302    if (!strcmp(type_name, "circle"))
5303        return PG_CIRCLE;
5304
5305    return PG_UNKNOWN;
5306}
5307/* }}} */
5308
5309/* {{{ php_pgsql_convert_match
5310 * test field value with regular expression specified.
5311 */
5312static int php_pgsql_convert_match(const char *str, size_t str_len, const char *regex , int icase TSRMLS_DC)
5313{
5314    regex_t re;
5315    regmatch_t *subs;
5316    int regopt = REG_EXTENDED;
5317    int regerr, ret = SUCCESS;
5318    int i;
5319
5320    /* Check invalid chars for POSIX regex */
5321    for (i = 0; i < str_len; i++) {
5322        if (str[i] == '\n' ||
5323            str[i] == '\r' ||
5324            str[i] == '\0' ) {
5325            return FAILURE;
5326        }
5327    }
5328
5329    if (icase) {
5330        regopt |= REG_ICASE;
5331    }
5332
5333    regerr = regcomp(&re, regex, regopt);
5334    if (regerr) {
5335        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot compile regex");
5336        regfree(&re);
5337        return FAILURE;
5338    }
5339    subs = (regmatch_t *)ecalloc(sizeof(regmatch_t), re.re_nsub+1);
5340
5341    regerr = regexec(&re, str, re.re_nsub+1, subs, 0);
5342    if (regerr == REG_NOMATCH) {
5343#ifdef PHP_DEBUG
5344        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "'%s' does not match with '%s'", str, regex);
5345#endif
5346        ret = FAILURE;
5347    }
5348    else if (regerr) {
5349        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot exec regex");
5350        ret = FAILURE;
5351    }
5352    regfree(&re);
5353    efree(subs);
5354    return ret;
5355}
5356
5357/* }}} */
5358
5359/* {{{ php_pgsql_add_quote
5360 * add quotes around string.
5361 */
5362static int php_pgsql_add_quotes(zval *src, zend_bool should_free TSRMLS_DC)
5363{
5364    smart_str str = {0};
5365
5366    assert(Z_TYPE_P(src) == IS_STRING);
5367    assert(should_free == 1 || should_free == 0);
5368
5369    smart_str_appendc(&str, 'E');
5370    smart_str_appendc(&str, '\'');
5371    smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
5372    smart_str_appendc(&str, '\'');
5373    smart_str_0(&str);
5374
5375    if (should_free) {
5376        efree(Z_STRVAL_P(src));
5377    }
5378    Z_STRVAL_P(src) = str.c;
5379    Z_STRLEN_P(src) = str.len;
5380
5381    return SUCCESS;
5382}
5383/* }}} */
5384
5385#define PGSQL_CONV_CHECK_IGNORE() \
5386                if (!err && Z_TYPE_P(new_val) == IS_STRING && !strcmp(Z_STRVAL_P(new_val), "NULL")) { \
5387                    /* if new_value is string "NULL" and field has default value, remove element to use default value */ \
5388                    if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_BVAL_PP(has_default)) { \
5389                        zval_dtor(new_val); \
5390                        FREE_ZVAL(new_val); \
5391                        skip_field = 1; \
5392                    } \
5393                    /* raise error if it's not null and cannot be ignored */ \
5394                    else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_BVAL_PP(not_null)) { \
5395                        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", field ); \
5396                        err = 1; \
5397                    } \
5398                }
5399
5400/* {{{ php_pgsql_convert
5401 * check and convert array values (fieldname=>vlaue pair) for sql
5402 */
5403PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, ulong opt TSRMLS_DC)
5404{
5405    HashPosition pos;
5406    char *field = NULL;
5407    uint field_len = -1;
5408    ulong num_idx = -1;
5409    zval *meta, **def, **type, **not_null, **has_default, **is_enum, **val, *new_val;
5410    int key_type, err = 0, skip_field;
5411    php_pgsql_data_type data_type;
5412
5413    assert(pg_link != NULL);
5414    assert(Z_TYPE_P(values) == IS_ARRAY);
5415    assert(Z_TYPE_P(result) == IS_ARRAY);
5416    assert(!(opt & ~PGSQL_CONV_OPTS));
5417
5418    if (!table_name) {
5419        return FAILURE;
5420    }
5421    MAKE_STD_ZVAL(meta);
5422    array_init(meta);
5423
5424/* table_name is escaped by php_pgsql_meta_data */
5425    if (php_pgsql_meta_data(pg_link, table_name, meta TSRMLS_CC) == FAILURE) {
5426        zval_dtor(meta);
5427        FREE_ZVAL(meta);
5428        return FAILURE;
5429    }
5430    for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos);
5431         zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&val, &pos) == SUCCESS;
5432         zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos)) {
5433        skip_field = 0;
5434        new_val = NULL;
5435
5436        if ((key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &field, &field_len, &num_idx, 0, &pos)) == HASH_KEY_NON_EXISTANT) {
5437            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get array key type");
5438            err = 1;
5439        }
5440        if (!err && key_type == HASH_KEY_IS_LONG) {
5441            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Accepts only string key for values");
5442            err = 1;
5443        }
5444        if (!err && key_type == HASH_KEY_NON_EXISTANT) {
5445            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Accepts only string key for values");
5446            err = 1;
5447        }
5448        if (!err && zend_hash_find(Z_ARRVAL_P(meta), field, field_len, (void **)&def) == FAILURE) {
5449            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid field name (%s) in values", field);
5450            err = 1;
5451        }
5452        if (!err && zend_hash_find(Z_ARRVAL_PP(def), "type", sizeof("type"), (void **)&type) == FAILURE) {
5453            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'type'");
5454            err = 1;
5455        }
5456        if (!err && zend_hash_find(Z_ARRVAL_PP(def), "not null", sizeof("not null"), (void **)&not_null) == FAILURE) {
5457            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'not null'");
5458            err = 1;
5459        }
5460        if (!err && zend_hash_find(Z_ARRVAL_PP(def), "has default", sizeof("has default"), (void **)&has_default) == FAILURE) {
5461            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'has default'");
5462            err = 1;
5463        }
5464        if (!err && zend_hash_find(Z_ARRVAL_PP(def), "is enum", sizeof("is enum"), (void **)&is_enum) == FAILURE) {
5465            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'is enum'");
5466            err = 1;
5467        }
5468        if (!err && (Z_TYPE_PP(val) == IS_ARRAY ||
5469             Z_TYPE_PP(val) == IS_OBJECT ||
5470             Z_TYPE_PP(val) == IS_CONSTANT_ARRAY)) {
5471            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scalar values as field values");
5472            err = 1;
5473        }
5474        if (err) {
5475            break; /* break out for() */
5476        }
5477        ALLOC_INIT_ZVAL(new_val);
5478
5479        if (Z_BVAL_PP(is_enum)) {
5480            /* enums need to be treated like strings */
5481            data_type = PG_TEXT;
5482        }
5483        else {
5484            data_type = php_pgsql_get_data_type(Z_STRVAL_PP(type), Z_STRLEN_PP(type));
5485        }
5486
5487        switch(data_type)
5488        {
5489            case PG_BOOL:
5490                switch (Z_TYPE_PP(val)) {
5491                    case IS_STRING:
5492                        if (Z_STRLEN_PP(val) == 0) {
5493                            ZVAL_STRING(new_val, "NULL", 1);
5494                        }
5495                        else {
5496                            if (!strcmp(Z_STRVAL_PP(val), "t") || !strcmp(Z_STRVAL_PP(val), "T") ||
5497                                !strcmp(Z_STRVAL_PP(val), "y") || !strcmp(Z_STRVAL_PP(val), "Y") ||
5498                                !strcmp(Z_STRVAL_PP(val), "true") || !strcmp(Z_STRVAL_PP(val), "True") ||
5499                                !strcmp(Z_STRVAL_PP(val), "yes") || !strcmp(Z_STRVAL_PP(val), "Yes") ||
5500                                !strcmp(Z_STRVAL_PP(val), "1")) {
5501                                ZVAL_STRING(new_val, "'t'", 1);
5502                            }
5503                            else if (!strcmp(Z_STRVAL_PP(val), "f") || !strcmp(Z_STRVAL_PP(val), "F") ||
5504                                     !strcmp(Z_STRVAL_PP(val), "n") || !strcmp(Z_STRVAL_PP(val), "N") ||
5505                                     !strcmp(Z_STRVAL_PP(val), "false") ||  !strcmp(Z_STRVAL_PP(val), "False") ||
5506                                     !strcmp(Z_STRVAL_PP(val), "no") ||  !strcmp(Z_STRVAL_PP(val), "No") ||
5507                                     !strcmp(Z_STRVAL_PP(val), "0")) {
5508                                ZVAL_STRING(new_val, "'f'", 1);
5509                            }
5510                            else {
5511                                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);
5512                                err = 1;
5513                            }
5514                        }
5515                        break;
5516
5517                    case IS_LONG:
5518                    case IS_BOOL:
5519                        if (Z_LVAL_PP(val)) {
5520                            ZVAL_STRING(new_val, "'t'", 1);
5521                        }
5522                        else {
5523                            ZVAL_STRING(new_val, "'f'", 1);
5524                        }
5525                        break;
5526
5527                    case IS_NULL:
5528                        ZVAL_STRING(new_val, "NULL", 1);
5529                        break;
5530
5531                    default:
5532                        err = 1;
5533                }
5534                PGSQL_CONV_CHECK_IGNORE();
5535                if (err) {
5536                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
5537                }
5538                break;
5539
5540            case PG_OID:
5541            case PG_INT2:
5542            case PG_INT4:
5543            case PG_INT8:
5544                switch (Z_TYPE_PP(val)) {
5545                    case IS_STRING:
5546                        if (Z_STRLEN_PP(val) == 0) {
5547                            ZVAL_STRING(new_val, "NULL", 1);
5548                        }
5549                        else {
5550                            /* FIXME: better regex must be used */
5551                            if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^([+-]{0,1}[0-9]+)$", 0 TSRMLS_CC) == FAILURE) {
5552                                err = 1;
5553                            }
5554                            else {
5555                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5556                            }
5557                        }
5558                        break;
5559
5560                    case IS_DOUBLE:
5561                        ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
5562                        convert_to_long_ex(&new_val);
5563                        break;
5564
5565                    case IS_LONG:
5566                        ZVAL_LONG(new_val, Z_LVAL_PP(val));
5567                        break;
5568
5569                    case IS_NULL:
5570                        ZVAL_STRING(new_val, "NULL", 1);
5571                        break;
5572
5573                    default:
5574                        err = 1;
5575                }
5576                PGSQL_CONV_CHECK_IGNORE();
5577                if (err) {
5578                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for pgsql '%s' (%s)", Z_STRVAL_PP(type), field);
5579                }
5580                break;
5581
5582            case PG_NUMERIC:
5583            case PG_MONEY:
5584            case PG_FLOAT4:
5585            case PG_FLOAT8:
5586                switch (Z_TYPE_PP(val)) {
5587                    case IS_STRING:
5588                        if (Z_STRLEN_PP(val) == 0) {
5589                            ZVAL_STRING(new_val, "NULL", 1);
5590                        }
5591                        else {
5592                            /* FIXME: better regex must be used */
5593                            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) {
5594                                err = 1;
5595                            }
5596                            else {
5597                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5598                            }
5599                        }
5600                        break;
5601
5602                    case IS_LONG:
5603                        ZVAL_LONG(new_val, Z_LVAL_PP(val));
5604                        break;
5605
5606                    case IS_DOUBLE:
5607                        ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
5608                        break;
5609
5610                    case IS_NULL:
5611                        ZVAL_STRING(new_val, "NULL", 1);
5612                        break;
5613
5614                    default:
5615                        err = 1;
5616                }
5617                PGSQL_CONV_CHECK_IGNORE();
5618                if (err) {
5619                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
5620                }
5621                break;
5622
5623            case PG_TEXT:
5624            case PG_CHAR:
5625            case PG_VARCHAR:
5626                switch (Z_TYPE_PP(val)) {
5627                    case IS_STRING:
5628                        if (Z_STRLEN_PP(val) == 0) {
5629                            if (opt & PGSQL_CONV_FORCE_NULL) {
5630                                ZVAL_STRING(new_val, "NULL", 1);
5631                            } else {
5632                                ZVAL_STRING(new_val, "''", 1);
5633                            }
5634                        }
5635                        else {
5636                            char *tmp;
5637                            Z_TYPE_P(new_val) = IS_STRING;
5638                            tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1);
5639                            Z_STRLEN_P(new_val) = (int)PQescapeStringConn(pg_link, tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val), NULL);
5640                            Z_STRVAL_P(new_val) = tmp;
5641                            php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5642                        }
5643                        break;
5644
5645                    case IS_LONG:
5646                        ZVAL_LONG(new_val, Z_LVAL_PP(val));
5647                        convert_to_string_ex(&new_val);
5648                        break;
5649
5650                    case IS_DOUBLE:
5651                        ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
5652                        convert_to_string_ex(&new_val);
5653                        break;
5654
5655                    case IS_NULL:
5656                        ZVAL_STRING(new_val, "NULL", 1);
5657                        break;
5658
5659                    default:
5660                        err = 1;
5661                }
5662                PGSQL_CONV_CHECK_IGNORE();
5663                if (err) {
5664                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
5665                }
5666                break;
5667
5668            case PG_UNIX_TIME:
5669            case PG_UNIX_TIME_INTERVAL:
5670                /* these are the actallay a integer */
5671                switch (Z_TYPE_PP(val)) {
5672                    case IS_STRING:
5673                        if (Z_STRLEN_PP(val) == 0) {
5674                            ZVAL_STRING(new_val, "NULL", 1);
5675                        }
5676                        else {
5677                            /* FIXME: Better regex must be used */
5678                            if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^[0-9]+$", 0 TSRMLS_CC) == FAILURE) {
5679                                err = 1;
5680                            }
5681                            else {
5682                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5683                                convert_to_long_ex(&new_val);
5684                            }
5685                        }
5686                        break;
5687
5688                    case IS_DOUBLE:
5689                        ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
5690                        convert_to_long_ex(&new_val);
5691                        break;
5692
5693                    case IS_LONG:
5694                        ZVAL_LONG(new_val, Z_LVAL_PP(val));
5695                        break;
5696
5697                    case IS_NULL:
5698                        ZVAL_STRING(new_val, "NULL", 1);
5699                        break;
5700
5701                    default:
5702                        err = 1;
5703                }
5704                PGSQL_CONV_CHECK_IGNORE();
5705                if (err) {
5706                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_PP(type), field);
5707                }
5708                break;
5709
5710            case PG_CIDR:
5711            case PG_INET:
5712                switch (Z_TYPE_PP(val)) {
5713                    case IS_STRING:
5714                        if (Z_STRLEN_PP(val) == 0) {
5715                            ZVAL_STRING(new_val, "NULL", 1);
5716                        }
5717                        else {
5718                            /* FIXME: Better regex must be used */
5719                            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) {
5720                                err = 1;
5721                            }
5722                            else {
5723                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5724                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5725                            }
5726                        }
5727                        break;
5728
5729                    case IS_NULL:
5730                        ZVAL_STRING(new_val, "NULL", 1);
5731                        break;
5732
5733                    default:
5734                        err = 1;
5735                }
5736                PGSQL_CONV_CHECK_IGNORE();
5737                if (err) {
5738                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for '%s' (%s)", Z_STRVAL_PP(type), field);
5739                }
5740                break;
5741
5742            case PG_TIME_WITH_TIMEZONE:
5743            case PG_TIMESTAMP:
5744            case PG_TIMESTAMP_WITH_TIMEZONE:
5745                switch(Z_TYPE_PP(val)) {
5746                    case IS_STRING:
5747                        if (Z_STRLEN_PP(val) == 0) {
5748                            ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1);
5749                        } else if (!strcasecmp(Z_STRVAL_PP(val), "now()")) {
5750                            ZVAL_STRINGL(new_val, "NOW()", sizeof("NOW()")-1, 1);
5751                        } else {
5752                            /* FIXME: better regex must be used */
5753                            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) {
5754                                err = 1;
5755                            } else {
5756                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5757                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5758                            }
5759                        }
5760                        break;
5761
5762                    case IS_NULL:
5763                        ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1);
5764                        break;
5765
5766                    default:
5767                        err = 1;
5768                }
5769                PGSQL_CONV_CHECK_IGNORE();
5770                if (err) {
5771                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
5772                }
5773                break;
5774
5775            case PG_DATE:
5776                switch(Z_TYPE_PP(val)) {
5777                    case IS_STRING:
5778                        if (Z_STRLEN_PP(val) == 0) {
5779                            ZVAL_STRING(new_val, "NULL", 1);
5780                        }
5781                        else {
5782                            /* FIXME: better regex must be used */
5783                            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) {
5784                                err = 1;
5785                            }
5786                            else {
5787                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5788                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5789                            }
5790                        }
5791                        break;
5792
5793                    case IS_NULL:
5794                        ZVAL_STRING(new_val, "NULL", 1);
5795                        break;
5796
5797                    default:
5798                        err = 1;
5799                }
5800                PGSQL_CONV_CHECK_IGNORE();
5801                if (err) {
5802                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
5803                }
5804                break;
5805
5806            case PG_TIME:
5807                switch(Z_TYPE_PP(val)) {
5808                    case IS_STRING:
5809                        if (Z_STRLEN_PP(val) == 0) {
5810                            ZVAL_STRING(new_val, "NULL", 1);
5811                        }
5812                        else {
5813                            /* FIXME: better regex must be used */
5814                            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) {
5815                                err = 1;
5816                            }
5817                            else {
5818                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5819                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5820                            }
5821                        }
5822                        break;
5823
5824                    case IS_NULL:
5825                        ZVAL_STRING(new_val, "NULL", 1);
5826                        break;
5827
5828                    default:
5829                        err = 1;
5830                }
5831                PGSQL_CONV_CHECK_IGNORE();
5832                if (err) {
5833                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
5834                }
5835                break;
5836
5837            case PG_INTERVAL:
5838                switch(Z_TYPE_PP(val)) {
5839                    case IS_STRING:
5840                        if (Z_STRLEN_PP(val) == 0) {
5841                            ZVAL_STRING(new_val, "NULL", 1);
5842                        }
5843                        else {
5844
5845                            /* From the Postgres docs:
5846
5847                               interval values can be written with the following syntax:
5848                               [@] quantity unit [quantity unit...] [direction]
5849
5850                               Where: quantity is a number (possibly signed); unit is second, minute, hour,
5851                               day, week, month, year, decade, century, millennium, or abbreviations or
5852                               plurals of these units [note not *all* abbreviations] ; direction can be
5853                               ago or empty. The at sign (@) is optional noise.
5854
5855                               ...
5856
5857                               Quantities of days, hours, minutes, and seconds can be specified without explicit
5858                               unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
5859                               sec'.
5860                            */
5861                            if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val),
5862                                                        "^(@?[ \\t]+)?("
5863
5864                                                        /* Textual time units and their abbreviations: */
5865                                                        "(([-+]?[ \\t]+)?"
5866                                                        "[0-9]+(\\.[0-9]*)?[ \\t]*"
5867                                                        "(millenniums|millennia|millennium|mil|mils|"
5868                                                        "centuries|century|cent|c|"
5869                                                        "decades|decade|dec|decs|"
5870                                                        "years|year|y|"
5871                                                        "months|month|mon|"
5872                                                        "weeks|week|w|"
5873                                                        "days|day|d|"
5874                                                        "hours|hour|hr|hrs|h|"
5875                                                        "minutes|minute|mins|min|m|"
5876                                                        "seconds|second|secs|sec|s))+|"
5877
5878                                                        /* Textual time units plus (dd)* hh[:mm[:ss]] */
5879                                                        "((([-+]?[ \\t]+)?"
5880                                                        "[0-9]+(\\.[0-9]*)?[ \\t]*"
5881                                                        "(millenniums|millennia|millennium|mil|mils|"
5882                                                        "centuries|century|cent|c|"
5883                                                        "decades|decade|dec|decs|"
5884                                                        "years|year|y|"
5885                                                        "months|month|mon|"
5886                                                        "weeks|week|w|"
5887                                                        "days|day|d))+"
5888                                                        "([-+]?[ \\t]+"
5889                                                        "([0-9]+[ \\t]+)+"               /* dd */
5890                                                        "(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */
5891                                                        ")?))"
5892                                                        "([ \\t]+ago)?$",
5893                                                        1 TSRMLS_CC) == FAILURE) {
5894                                err = 1;
5895                            }
5896                            else {
5897                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5898                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5899                            }
5900                        }
5901                        break;
5902
5903                    case IS_NULL:
5904                        ZVAL_STRING(new_val, "NULL", 1);
5905                        break;
5906
5907                    default:
5908                        err = 1;
5909                }
5910                PGSQL_CONV_CHECK_IGNORE();
5911                if (err) {
5912                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
5913                }
5914                break;
5915#ifdef HAVE_PQESCAPE
5916            case PG_BYTEA:
5917                switch (Z_TYPE_PP(val)) {
5918                    case IS_STRING:
5919                        if (Z_STRLEN_PP(val) == 0) {
5920                            ZVAL_STRING(new_val, "NULL", 1);
5921                        }
5922                        else {
5923                            unsigned char *tmp;
5924                            size_t to_len;
5925                            smart_str s = {0};
5926#ifdef HAVE_PQESCAPE_BYTEA_CONN
5927                            tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_PP(val), Z_STRLEN_PP(val), &to_len);
5928#else
5929                            tmp = PQescapeBytea(Z_STRVAL_PP(val), (unsigned char *)Z_STRLEN_PP(val), &to_len);
5930#endif
5931                            Z_TYPE_P(new_val) = IS_STRING;
5932                            Z_STRLEN_P(new_val) = to_len-1; /* PQescapeBytea's to_len includes additional '\0' */
5933                            Z_STRVAL_P(new_val) = emalloc(to_len);
5934                            memcpy(Z_STRVAL_P(new_val), tmp, to_len);
5935                            PQfreemem(tmp);
5936                            php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5937                            smart_str_appendl(&s, Z_STRVAL_P(new_val), Z_STRLEN_P(new_val));
5938                            smart_str_0(&s);
5939                            efree(Z_STRVAL_P(new_val));
5940                            Z_STRVAL_P(new_val) = s.c;
5941                            Z_STRLEN_P(new_val) = s.len;
5942                        }
5943                        break;
5944
5945                    case IS_LONG:
5946                        ZVAL_LONG(new_val, Z_LVAL_PP(val));
5947                        convert_to_string_ex(&new_val);
5948                        break;
5949
5950                    case IS_DOUBLE:
5951                        ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
5952                        convert_to_string_ex(&new_val);
5953                        break;
5954
5955                    case IS_NULL:
5956                        ZVAL_STRING(new_val, "NULL", 1);
5957                        break;
5958
5959                    default:
5960                        err = 1;
5961                }
5962                PGSQL_CONV_CHECK_IGNORE();
5963                if (err) {
5964                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
5965                }
5966                break;
5967
5968#endif
5969            case PG_MACADDR:
5970                switch(Z_TYPE_PP(val)) {
5971                    case IS_STRING:
5972                        if (Z_STRLEN_PP(val) == 0) {
5973                            ZVAL_STRING(new_val, "NULL", 1);
5974                        }
5975                        else {
5976                            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) {
5977                                err = 1;
5978                            }
5979                            else {
5980                                ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
5981                                php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
5982                            }
5983                        }
5984                        break;
5985
5986                    case IS_NULL:
5987                        ZVAL_STRING(new_val, "NULL", 1);
5988                        break;
5989
5990                    default:
5991                        err = 1;
5992                }
5993                PGSQL_CONV_CHECK_IGNORE();
5994                if (err) {
5995                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
5996                }
5997                break;
5998
5999                /* bit */
6000            case PG_BIT:
6001            case PG_VARBIT:
6002                /* geometric */
6003            case PG_LINE:
6004            case PG_LSEG:
6005            case PG_POINT:
6006            case PG_BOX:
6007            case PG_PATH:
6008            case PG_POLYGON:
6009            case PG_CIRCLE:
6010                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "PostgreSQL '%s' type (%s) is not supported", Z_STRVAL_PP(type), field);
6011                err = 1;
6012                break;
6013
6014            case PG_UNKNOWN:
6015            default:
6016                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown or system data type '%s' for '%s'", Z_STRVAL_PP(type), field);
6017                err = 1;
6018                break;
6019        } /* switch */
6020
6021        if (err) {
6022            zval_dtor(new_val);
6023            FREE_ZVAL(new_val);
6024            break; /* break out for() */
6025        }
6026        /* If field is NULL and HAS DEFAULT, should be skipped */
6027        if (!skip_field) {
6028            char *escaped;
6029            size_t field_len = strlen(field);
6030
6031            if (_php_pgsql_detect_identifier_escape(field, field_len) == SUCCESS) {
6032                add_assoc_zval(result, field, new_val);
6033            } else {
6034                escaped = PGSQLescapeIdentifier(pg_link, field, field_len);
6035                add_assoc_zval(result, escaped, new_val);
6036                PGSQLfree(escaped);
6037            }
6038        }
6039    } /* for */
6040    zval_dtor(meta);
6041    FREE_ZVAL(meta);
6042
6043    if (err) {
6044        /* shouldn't destroy & free zval here */
6045        return FAILURE;
6046    }
6047    return SUCCESS;
6048}
6049/* }}} */
6050
6051
6052/* {{{ proto array pg_convert(resource db, string table, array values[, int options])
6053   Check and convert values for PostgreSQL SQL statement */
6054PHP_FUNCTION(pg_convert)
6055{
6056    zval *pgsql_link, *values;
6057    char *table_name;
6058    int table_name_len;
6059    ulong option = 0;
6060    PGconn *pg_link;
6061    int id = -1;
6062
6063    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
6064                              "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
6065        return;
6066    }
6067    if (option & ~PGSQL_CONV_OPTS) {
6068        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
6069        RETURN_FALSE;
6070    }
6071    if (!table_name_len) {
6072        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Table name is invalid");
6073        RETURN_FALSE;
6074    }
6075
6076    ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
6077
6078    if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
6079        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
6080    }
6081    array_init(return_value);
6082    if (php_pgsql_convert(pg_link, table_name, values, return_value, option TSRMLS_CC) == FAILURE) {
6083        zval_dtor(return_value);
6084        RETURN_FALSE;
6085    }
6086}
6087/* }}} */
6088
6089static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, ulong opt TSRMLS_DC)
6090{
6091    if (opt & PGSQL_DML_ASYNC) {
6092        if (PQsendQuery(pg_link, querystr->c)) {
6093            return 0;
6094        }
6095    }
6096    else {
6097        PGresult *pg_result;
6098
6099        pg_result = PQexec(pg_link, querystr->c);
6100        if (PQresultStatus(pg_result) == expect) {
6101            PQclear(pg_result);
6102            return 0;
6103        } else {
6104            php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", PQresultErrorMessage(pg_result));
6105            PQclear(pg_result);
6106        }
6107    }
6108
6109    return -1;
6110}
6111
6112static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table)
6113{
6114    char *table_copy, *escaped, *token, *tmp;
6115    size_t len;
6116
6117    /* schame.table should be "schame"."table" */
6118    table_copy = estrdup(table);
6119    token = php_strtok_r(table_copy, ".", &tmp);
6120    len = strlen(token);
6121    if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) {
6122        smart_str_appendl(querystr, token, len);
6123    } else {
6124        escaped = PGSQLescapeIdentifier(pg_link, token, len);
6125        smart_str_appends(querystr, escaped);
6126        PGSQLfree(escaped);
6127    }
6128    if (tmp && *tmp) {
6129        len = strlen(tmp);
6130        /* "schema"."table" format */
6131        if (_php_pgsql_detect_identifier_escape(tmp, len) == SUCCESS) {
6132            smart_str_appendc(querystr, '.');
6133            smart_str_appendl(querystr, tmp, len);
6134        } else {
6135            escaped = PGSQLescapeIdentifier(pg_link, tmp, len);
6136            smart_str_appendc(querystr, '.');
6137            smart_str_appends(querystr, escaped);
6138            PGSQLfree(escaped);
6139        }
6140    }
6141    efree(table_copy);
6142}
6143
6144/* {{{ php_pgsql_insert
6145 */
6146PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, ulong opt, char **sql TSRMLS_DC)
6147{
6148    zval **val, *converted = NULL;
6149    char buf[256];
6150    char *fld;
6151    smart_str querystr = {0};
6152    int key_type, ret = FAILURE;
6153    uint fld_len;
6154    ulong num_idx;
6155    HashPosition pos;
6156
6157    assert(pg_link != NULL);
6158    assert(table != NULL);
6159    assert(Z_TYPE_P(var_array) == IS_ARRAY);
6160
6161    if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
6162        smart_str_appends(&querystr, "INSERT INTO ");
6163        build_tablename(&querystr, pg_link, table);
6164        smart_str_appends(&querystr, " DEFAULT VALUES");
6165
6166        goto no_values;
6167    }
6168
6169    /* convert input array if needed */
6170    if (!(opt & PGSQL_DML_NO_CONV)) {
6171        MAKE_STD_ZVAL(converted);
6172        array_init(converted