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