diff options
Diffstat (limited to 'ext/pgsql/pgsql.c')
-rw-r--r-- | ext/pgsql/pgsql.c | 1470 |
1 files changed, 908 insertions, 562 deletions
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 8ca1238777..fcf5b4799b 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -15,8 +15,8 @@ | Authors: Zeev Suraski <zeev@zend.com> | | Jouni Ahto <jouni.ahto@exdec.fi> | | Yasuo Ohgaki <yohgaki@php.net> | - | Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*) | - | Chris Kings-Lynne <chriskl@php.net> (v3 protocol) | + | Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*) | + | Chris Kings-Lynne <chriskl@php.net> (v3 protocol) | +----------------------------------------------------------------------+ */ @@ -37,6 +37,9 @@ #include "ext/standard/php_standard.h" #include "ext/standard/php_smart_str.h" #include "ext/ereg/php_regex.h" +#ifdef PHP_WIN32 +# include "win32/time.h" +#endif #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME @@ -112,6 +115,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1) ZEND_ARG_INFO(0, database) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect_poll, 0, 0, 0) + ZEND_ARG_INFO(0, connection) +ZEND_END_ARG_INFO() + #if HAVE_PQPARAMETERSTATUS ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1) ZEND_ARG_INFO(0, connection) @@ -523,6 +530,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0) ZEND_ARG_INFO(0, connection) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_socket, 0, 0, 1) + ZEND_ARG_INFO(0, connection) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_consume_input, 0, 0, 1) + ZEND_ARG_INFO(0, connection) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_flush, 0, 0, 1) + ZEND_ARG_INFO(0, connection) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2) ZEND_ARG_INFO(0, db) ZEND_ARG_INFO(0, table) @@ -571,6 +590,7 @@ const zend_function_entry pgsql_functions[] = { /* connection functions */ PHP_FE(pg_connect, arginfo_pg_connect) PHP_FE(pg_pconnect, arginfo_pg_pconnect) + PHP_FE(pg_connect_poll, arginfo_pg_connect_poll) PHP_FE(pg_close, arginfo_pg_close) PHP_FE(pg_connection_status, arginfo_pg_connection_status) PHP_FE(pg_connection_busy, arginfo_pg_connection_busy) @@ -640,6 +660,9 @@ const zend_function_entry pgsql_functions[] = { #endif /* async message function */ PHP_FE(pg_get_notify, arginfo_pg_get_notify) + PHP_FE(pg_socket, arginfo_pg_socket) + PHP_FE(pg_consume_input,arginfo_pg_consume_input) + PHP_FE(pg_flush, arginfo_pg_flush) PHP_FE(pg_get_pid, arginfo_pg_get_pid) /* error message functions */ PHP_FE(pg_result_error, arginfo_pg_result_error) @@ -753,6 +776,105 @@ ZEND_GET_MODULE(pgsql) static int le_link, le_plink, le_result, le_lofp, le_string; +/* Compatibility definitions */ + +#ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT +#define pg_encoding_to_char(x) "SQL_ASCII" +#endif + +#if !HAVE_PQESCAPE_CONN +#define PQescapeStringConn(conn, to, form, len, error) PQescapeString(to, from, len) +#endif + +#if HAVE_PQESCAPELITERAL +#define PGSQLescapeLiteral(conn, str, len) PQescapeLiteral(conn, str, len) +#define PGSQLescapeIdentifier(conn, str, len) PQescapeIdentifier(conn, str, len) +#define PGSQLfree(a) PQfreemem(a) +#else +#define PGSQLescapeLiteral(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 0) +#define PGSQLescapeLiteral2(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 1) +#define PGSQLescapeIdentifier(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 0, 0) +#define PGSQLfree(a) efree(a) + +/* emulate libpq's PQescapeInternal() 9.0 or later */ +static char* php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe) { + char *result, *rp, *s; + size_t tmp_len; + + if (!conn) { + return NULL; + } + + /* allocate enough memory */ + rp = result = (char *)safe_emalloc(len, 2, 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */ + + if (escape_literal) { + size_t new_len; + + if (safe) { + char *tmp = (char *)safe_emalloc(len, 2, 1); + *rp++ = '\''; + /* PQescapeString does not escape \, but it handles multibyte chars safely. + This escape is incompatible with PQescapeLiteral. */ + new_len = PQescapeStringConn(conn, tmp, str, len, NULL); + strncpy(rp, tmp, new_len); + efree(tmp); + rp += new_len; + } else { + char *encoding; + /* This is compatible with PQescapeLiteral, but it cannot handle multbyte chars + such as SJIS, BIG5. Raise warning and return NULL by checking + client_encoding. */ + encoding = (char *) pg_encoding_to_char(PQclientEncoding(conn)); + if (!strncmp(encoding, "SJIS", sizeof("SJIS")-1) || + !strncmp(encoding, "SHIFT_JIS_2004", sizeof("SHIFT_JIS_2004")-1) || + !strncmp(encoding, "BIG5", sizeof("BIG5")-1) || + !strncmp(encoding, "GB18030", sizeof("GB18030")-1) || + !strncmp(encoding, "GBK", sizeof("GBK")-1) || + !strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) || + !strncmp(encoding, "UHC", sizeof("UHC")-1) ) { + TSRMLS_FETCH(); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsafe encoding is used. Do not use '%s' encoding or use PostgreSQL 9.0 or later libpq.", encoding); + } + /* check backslashes */ + tmp_len = strspn(str, "\\"); + if (tmp_len != len) { + /* add " E" for escaping slashes */ + *rp++ = ' '; + *rp++ = 'E'; + } + *rp++ = '\''; + for (s = (char *)str; s - str < len; ++s) { + if (*s == '\'' || *s == '\\') { + *rp++ = *s; + *rp++ = *s; + } else { + *rp++ = *s; + } + } + } + *rp++ = '\''; + } else { + /* Identifier escape. */ + *rp++ = '"'; + for (s = (char *)str; s - str < len; ++s) { + if (*s == '"') { + *rp++ = '"'; + *rp++ = '"'; + } else { + *rp++ = *s; + } + } + *rp++ = '"'; + } + *rp = '\0'; + + return result; +} +#endif + + /* {{{ _php_pgsql_trim_message */ static char * _php_pgsql_trim_message(const char *message, int *len) { @@ -781,16 +903,16 @@ static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf) #define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf) -#define PHP_PQ_ERROR(text, pgsql) { \ - char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \ - php_error_docref(NULL TSRMLS_CC, E_WARNING, text, msgbuf); \ - efree(msgbuf); \ +#define PHP_PQ_ERROR(text, pgsql) { \ + char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \ + php_error_docref(NULL TSRMLS_CC, E_WARNING, text, msgbuf); \ + efree(msgbuf); \ } \ /* {{{ php_pgsql_set_default_link */ static void php_pgsql_set_default_link(int id TSRMLS_DC) -{ +{ zend_list_addref(id); if (PGG(default_link) != -1) { @@ -858,10 +980,10 @@ static void _php_pgsql_notice_ptr_dtor(void **ptr) { php_pgsql_notice *notice = (php_pgsql_notice *)*ptr; if (notice) { - efree(notice->message); - efree(notice); - notice = NULL; - } + efree(notice->message); + efree(notice); + notice = NULL; + } } /* }}} */ @@ -951,57 +1073,7 @@ static int _php_pgsql_detect_identifier_escape(const char *identifier, size_t le return SUCCESS; } -#if !HAVE_PQESCAPELITERAL -/* {{{ _php_pgsql_escape_identifier - * Since PQescapeIdentifier() is unavailable (PostgreSQL 9.0 <), idenfifers - * should be escaped by pgsql module. - * Note: this function does not care for encoding. Therefore users should not - * use this with SJIS/BIG5 etc. (i.e. Encoding base injection may possible with - * before PostgreSQL 9.0) - */ -static char *_php_pgsql_escape_identifier(const char *field, size_t field_len) -{ - ulong field_escaped_len = field_len*2 + 3; - ulong i, j = 0; - char *field_escaped; - - field_escaped = (char *)malloc(field_escaped_len); - field_escaped[j++] = '"'; - for (i = 0; i < field_len; i++) { - if (field[i] == '"') { - field_escaped[j++] = '"'; - field_escaped[j++] = '"'; - } else { - field_escaped[j++] = field[i]; - } - } - field_escaped[j++] = '"'; - field_escaped[j] = '\0'; - return field_escaped; -} -/* }}} */ -#endif - -/* {{{ _php_pgsql_strndup, no strndup should be used */ -static char *_php_pgsql_strndup(const char *s, size_t len) -{ - char *new; - - if (NULL == s) { - return (char *)NULL; - } - - new = (char *) malloc(len + 1); - - if (NULL == new) { - return (char *)NULL; - } - new[len] = '\0'; - - return memmove(new, s, len); -} -/* }}} */ /* {{{ PHP_INI */ @@ -1030,7 +1102,7 @@ static PHP_GINIT_FUNCTION(pgsql) PHP_MINIT_FUNCTION(pgsql) { REGISTER_INI_ENTRIES(); - + le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number); le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number); le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number); @@ -1043,6 +1115,7 @@ PHP_MINIT_FUNCTION(pgsql) #endif /* For connection option */ REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECT_ASYNC", PGSQL_CONNECT_ASYNC, CONST_CS | CONST_PERSISTENT); /* For pg_fetch_array() */ REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT); @@ -1050,6 +1123,18 @@ PHP_MINIT_FUNCTION(pgsql) /* For pg_connection_status() */ REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_STARTED", CONNECTION_STARTED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_MADE", CONNECTION_MADE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AWAITING_RESPONSE", CONNECTION_AWAITING_RESPONSE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AUTH_OK", CONNECTION_AUTH_OK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SSL_STARTUP", CONNECTION_SSL_STARTUP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SETENV", CONNECTION_SETENV, CONST_CS | CONST_PERSISTENT); + /* For pg_connect_poll() */ + REGISTER_LONG_CONSTANT("PGSQL_POLLING_FAILED", PGRES_POLLING_FAILED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_POLLING_READING", PGRES_POLLING_READING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_POLLING_WRITING", PGRES_POLLING_WRITING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_POLLING_OK", PGRES_POLLING_OK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PGSQL_POLLING_ACTIVE", PGRES_POLLING_ACTIVE, CONST_CS | CONST_PERSISTENT); #if HAVE_PGTRANSACTIONSTATUS /* For pg_transaction_status() */ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT); @@ -1104,6 +1189,7 @@ PHP_MINIT_FUNCTION(pgsql) REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT); /* pg_insert/update/delete/select options */ + REGISTER_LONG_CONSTANT("PGSQL_DML_ESCAPE", PGSQL_DML_ESCAPE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT); @@ -1166,7 +1252,7 @@ PHP_MINFO_FUNCTION(pgsql) #else php_info_print_table_row(2, "SSL support", "disabled"); #endif -#endif /* HAVE_PG_CONFIG_H */ +#endif /* HAVE_PG_CONFIG_H */ snprintf(buf, sizeof(buf), "%ld", PGG(num_persistent)); php_info_print_table_row(2, "Active Persistent Links", buf); snprintf(buf, sizeof(buf), "%ld", PGG(num_links)); @@ -1234,7 +1320,7 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) break; } } - + if (persistent && PGG(allow_persistent)) { zend_rsrc_list_entry *le; @@ -1317,7 +1403,7 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) ZEND_REGISTER_RESOURCE(return_value, pgsql, le_plink); } else { /* Non persistent connection */ zend_rsrc_list_entry *index_ptr,new_index_ptr; - + /* first we check the hash for the hashed_details key. if it exists, * it should point us to the right offset where the actual pgsql link sits. * if it doesn't, open a new pgsql link, add it to the resource list, @@ -1348,17 +1434,35 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create new link. Too many open links (%ld)", PGG(num_links)); goto err; } - if (connstring) { - pgsql = PQconnectdb(connstring); + + /* Non-blocking connect */ + if (connect_type & PGSQL_CONNECT_ASYNC) { + if (connstring) { + pgsql = PQconnectStart(connstring); + if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) { + PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql); + if (pgsql) { + PQfinish(pgsql); + } + goto err; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection string required for async connections"); + goto err; + } } else { - pgsql = PQsetdb(host,port,options,tty,dbname); - } - if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) { - PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql); - if (pgsql) { - PQfinish(pgsql); + if (connstring) { + pgsql = PQconnectdb(connstring); + } else { + pgsql = PQsetdb(host,port,options,tty,dbname); + } + if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) { + PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql); + if (pgsql) { + PQfinish(pgsql); + } + goto err; } - goto err; } /* add it to the list */ @@ -1381,7 +1485,7 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) cleanup: smart_str_free(&str); return; - + err: smart_str_free(&str); RETURN_FALSE; @@ -1410,6 +1514,31 @@ PHP_FUNCTION(pg_connect) } /* }}} */ +/* {{{ proto resource pg_connect_poll(resource connection) + Poll the status of an in-progress async PostgreSQL connection attempt*/ +PHP_FUNCTION(pg_connect_poll) +{ + zval *pgsql_link; + int id = -1; + PGconn *pgsql; + int ret; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) { + return; + } + + if (pgsql_link == NULL && id == -1) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); + + ret = PQconnectPoll(pgsql); + + RETURN_LONG(ret); +} +/* }}} */ + /* {{{ proto resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database) Open a persistent PostgreSQL connection */ PHP_FUNCTION(pg_pconnect) @@ -1425,11 +1554,11 @@ PHP_FUNCTION(pg_close) zval *pgsql_link = NULL; int id = -1, argc = ZEND_NUM_ARGS(); PGconn *pgsql; - + if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) { return; } - + if (argc == 0) { id = PGG(default_link); CHECK_DEFAULT_LINK(id); @@ -1437,7 +1566,7 @@ PHP_FUNCTION(pg_close) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -1445,7 +1574,7 @@ PHP_FUNCTION(pg_close) zend_list_delete(Z_RESVAL_P(pgsql_link)); } - if (id!=-1 + if (id!=-1 || (pgsql_link && Z_RESVAL_P(pgsql_link)==PGG(default_link))) { zend_list_delete(PGG(default_link)); PGG(default_link) = -1; @@ -1464,6 +1593,7 @@ PHP_FUNCTION(pg_close) #define PHP_PG_HOST 6 #define PHP_PG_VERSION 7 + /* {{{ php_pgsql_get_link_info */ static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) @@ -1484,7 +1614,7 @@ static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -1632,7 +1762,7 @@ PHP_FUNCTION(pg_parameter_status) } if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -1663,7 +1793,7 @@ PHP_FUNCTION(pg_ping) } if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -1738,7 +1868,7 @@ PHP_FUNCTION(pg_query) } else { status = (ExecStatusType) PQstatus(pgsql); } - + switch (status) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: @@ -1834,7 +1964,7 @@ PHP_FUNCTION(pg_query_params) if (num_params > 0) { int i = 0; params = (char **)safe_emalloc(sizeof(char *), num_params, 0); - + for(i = 0; i < num_params; i++) { if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter"); @@ -1931,10 +2061,10 @@ PHP_FUNCTION(pg_prepare) return; } } - + if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -1961,7 +2091,7 @@ PHP_FUNCTION(pg_prepare) } else { status = (ExecStatusType) PQstatus(pgsql); } - + switch (status) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: @@ -2041,7 +2171,7 @@ PHP_FUNCTION(pg_execute) if (num_params > 0) { int i = 0; params = (char **)safe_emalloc(sizeof(char *), num_params, 0); - + for(i = 0; i < num_params; i++) { if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter"); @@ -2083,7 +2213,7 @@ PHP_FUNCTION(pg_execute) } else { status = (ExecStatusType) PQstatus(pgsql); } - + _php_pgsql_free_params(params, num_params); switch (status) { @@ -2235,7 +2365,7 @@ static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list TSRMLS_DC) num_rows = PQntuples(result); oid_offset = PQfnumber(result,"oid"); name_offset = PQfnumber(result,"typname"); - + for (i=0; i<num_rows; i++) { if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) { continue; @@ -2245,7 +2375,7 @@ static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list TSRMLS_DC) smart_str_appends(&str, "pgsql_oid_"); smart_str_appends(&str, tmp_oid); smart_str_0(&str); - + if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) { continue; } @@ -2262,7 +2392,7 @@ static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list TSRMLS_DC) smart_str_free(&str); return ret; } -/* }}} */ +/* }}} */ #ifdef HAVE_PQFTABLE /* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only]) @@ -2295,7 +2425,6 @@ PHP_FUNCTION(pg_field_table) RETURN_FALSE; } - if (return_oid) { #if UINT_MAX > LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */ if (oid > LONG_MAX) { @@ -2325,7 +2454,6 @@ PHP_FUNCTION(pg_field_table) smart_str_append_unsigned(&querystr, oid); smart_str_0(&querystr); - if ((tmp_res = PQexec(pg_result->conn, querystr.c)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { if (tmp_res) { PQclear(tmp_res); @@ -2353,8 +2481,8 @@ PHP_FUNCTION(pg_field_table) } } -/* }}} */ -#endif +/* }}} */ +#endif #define PHP_PG_FIELD_NAME 1 #define PHP_PG_FIELD_SIZE 2 @@ -2370,11 +2498,11 @@ static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_typ PGresult *pgsql_result; pgsql_result_handle *pg_result; Oid oid; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &result, &field) == FAILURE) { return; } - + ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result); pgsql_result = pg_result->result; @@ -2383,7 +2511,7 @@ static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_typ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad field offset specified"); RETURN_FALSE; } - + switch (entry_type) { case PHP_PG_FIELD_NAME: Z_STRVAL_P(return_value) = PQfname(pgsql_result, field); @@ -2470,11 +2598,11 @@ PHP_FUNCTION(pg_field_num) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &result, &field, &field_len) == FAILURE) { return; } - + ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result); pgsql_result = pg_result->result; - + Z_LVAL_P(return_value) = PQfnumber(pgsql_result, field); Z_TYPE_P(return_value) = IS_LONG; } @@ -2489,7 +2617,7 @@ PHP_FUNCTION(pg_fetch_result) PGresult *pgsql_result; pgsql_result_handle *pg_result; int field_offset, pgsql_row, argc = ZEND_NUM_ARGS(); - + if (argc == 2) { if (zend_parse_parameters(argc TSRMLS_CC, "rZ", &result, &field) == FAILURE) { return; @@ -2532,7 +2660,7 @@ PHP_FUNCTION(pg_fetch_result) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad column offset specified"); RETURN_FALSE; } - + if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) { Z_TYPE_P(return_value) = IS_NULL; } else { @@ -2630,10 +2758,13 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long result_type, if (element) { const uint element_len = strlen(element); + data = safe_estrndup(element, element_len); + data_len = element_len; + if (result_type & PGSQL_NUM) { add_index_stringl(return_value, i, element, element_len); } - + if (result_type & PGSQL_ASSOC) { field_name = PQfname(pgsql_result, i); add_assoc_stringl(return_value, field_name, element, element_len); @@ -2646,11 +2777,11 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long result_type, zval dataset = *return_value; zend_fcall_info fci; zend_fcall_info_cache fcc; - zval *retval_ptr; - + zval *retval_ptr; + object_and_properties_init(return_value, ce, NULL); zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC); - + if (ce->constructor) { fci.size = sizeof(fci); fci.function_table = &ce->function_table; @@ -2658,40 +2789,29 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long result_type, fci.symbol_table = NULL; fci.object_ptr = return_value; fci.retval_ptr_ptr = &retval_ptr; + fci.params = NULL; + fci.param_count = 0; + fci.no_separation = 1; + if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) { - if (Z_TYPE_P(ctor_params) == IS_ARRAY) { - HashTable *ht = Z_ARRVAL_P(ctor_params); - Bucket *p; - - fci.param_count = 0; - fci.params = safe_emalloc(sizeof(zval***), ht->nNumOfElements, 0); - p = ht->pListHead; - while (p != NULL) { - fci.params[fci.param_count++] = (zval**)p->pData; - p = p->pListNext; - } - } else { + if (zend_fcall_info_args(&fci, ctor_params TSRMLS_CC) == FAILURE) { /* Two problems why we throw exceptions here: PHP is typeless * and hence passing one argument that's not an array could be - * by mistake and the other way round is possible, too. The + * by mistake and the other way round is possible, too. The * single value is an array. Also we'd have to make that one * argument passed by reference. */ zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Parameter ctor_params must be an array", 0 TSRMLS_CC); return; } - } else { - fci.param_count = 0; - fci.params = NULL; } - fci.no_separation = 1; fcc.initialized = 1; fcc.function_handler = ce->constructor; fcc.calling_scope = EG(scope); fcc.called_scope = Z_OBJCE_P(return_value); fcc.object_ptr = return_value; - + if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) { 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); } else { @@ -2797,7 +2917,7 @@ PHP_FUNCTION(pg_fetch_all_columns) array_init(return_value); - if ((pg_numrows = PQntuples(pgsql_result)) <= 0) { + if ((pg_numrows = PQntuples(pgsql_result)) <= 0) { return; } @@ -2806,7 +2926,7 @@ PHP_FUNCTION(pg_fetch_all_columns) add_next_index_null(return_value); } else { add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, colno)); - } + } } } /* }}} */ @@ -2828,7 +2948,7 @@ PHP_FUNCTION(pg_result_seek) if (row < 0 || row >= PQntuples(pg_result->result)) { RETURN_FALSE; } - + /* seek to offset */ pg_result->row = row; RETURN_TRUE; @@ -2858,7 +2978,7 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) return; } } - + ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result); pgsql_result = pg_result->result; @@ -2878,7 +2998,7 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) RETURN_FALSE; } } - + switch(Z_TYPE_PP(field)) { case IS_STRING: convert_to_string_ex(field); @@ -2893,7 +3013,7 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad column offset specified"); RETURN_FALSE; } - + switch (entry_type) { case PHP_PG_DATA_LENGTH: Z_LVAL_P(return_value) = PQgetlength(pgsql_result, pgsql_row, field_offset); @@ -2928,7 +3048,7 @@ PHP_FUNCTION(pg_free_result) { zval *result; pgsql_result_handle *pg_result; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) { return; } @@ -2956,7 +3076,7 @@ PHP_FUNCTION(pg_last_oid) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) { return; } - + ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result); pgsql_result = pg_result->result; #ifdef HAVE_PQOIDVALUE @@ -2987,18 +3107,18 @@ PHP_FUNCTION(pg_trace) FILE *fp = NULL; php_stream *stream; id = PGG(default_link); - + if (zend_parse_parameters(argc TSRMLS_CC, "s|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) { return; } if (argc < 3) { CHECK_DEFAULT_LINK(id); - } + } if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3037,7 +3157,7 @@ PHP_FUNCTION(pg_untrace) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); PQuntrace(pgsql); @@ -3049,10 +3169,10 @@ PHP_FUNCTION(pg_untrace) Create a large object */ PHP_FUNCTION(pg_lo_create) { - zval *pgsql_link = NULL, *oid = NULL; - PGconn *pgsql; - Oid pgsql_oid, wanted_oid = InvalidOid; - int id = -1, argc = ZEND_NUM_ARGS(); + zval *pgsql_link = NULL, *oid = NULL; + PGconn *pgsql; + Oid pgsql_oid, wanted_oid = InvalidOid; + int id = -1, argc = ZEND_NUM_ARGS(); if (zend_parse_parameters(argc TSRMLS_CC, "|zz", &pgsql_link, &oid) == FAILURE) { return; @@ -3074,12 +3194,12 @@ PHP_FUNCTION(pg_lo_create) ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); if (oid) { -#ifndef HAVE_PG_LO_CREATE - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "OID value passing not supported"); +#ifndef HAVE_PG_LO_CREATE + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Passing OID value is not supported. Upgrade your PostgreSQL"); #else switch (Z_TYPE_P(oid)) { case IS_STRING: - { + { char *end_ptr; wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10); if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) { @@ -3105,7 +3225,7 @@ PHP_FUNCTION(pg_lo_create) RETURN_FALSE; } - PGSQL_RETURN_OID(pgsql_oid); + PGSQL_RETURN_OID(pgsql_oid); #endif } @@ -3114,7 +3234,7 @@ PHP_FUNCTION(pg_lo_create) RETURN_FALSE; } - PGSQL_RETURN_OID(pgsql_oid); + PGSQL_RETURN_OID(pgsql_oid); } /* }}} */ @@ -3176,7 +3296,7 @@ PHP_FUNCTION(pg_lo_unlink) } if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3248,7 +3368,7 @@ PHP_FUNCTION(pg_lo_open) } if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3340,8 +3460,8 @@ PHP_FUNCTION(pg_lo_close) Read a large object */ PHP_FUNCTION(pg_lo_read) { - zval *pgsql_id; - long len; + zval *pgsql_id; + long len; int buf_len = PGSQL_LO_READ_BUF_SIZE, nbytes, argc = ZEND_NUM_ARGS(); char *buf; pgLofp *pgsql; @@ -3468,7 +3588,7 @@ PHP_FUNCTION(pg_lo_import) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3479,7 +3599,7 @@ PHP_FUNCTION(pg_lo_import) Oid wanted_oid; switch (Z_TYPE_P(oid)) { case IS_STRING: - { + { char *end_ptr; wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10); if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) { @@ -3601,13 +3721,13 @@ PHP_FUNCTION(pg_lo_export) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); if (lo_export(pgsql, oid, file_out)) { RETURN_TRUE; - } + } RETURN_FALSE; } /* }}} */ @@ -3774,7 +3894,7 @@ PHP_FUNCTION(pg_set_client_encoding) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3808,10 +3928,6 @@ PHP_FUNCTION(pg_client_encoding) /* Just do the same as found in PostgreSQL sources... */ -#ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT -#define pg_encoding_to_char(x) "SQL_ASCII" -#endif - Z_STRVAL_P(return_value) = (char *) pg_encoding_to_char(PQclientEncoding(pgsql)); Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value)); Z_STRVAL_P(return_value) = (char *) estrdup(Z_STRVAL_P(return_value)); @@ -3844,7 +3960,7 @@ PHP_FUNCTION(pg_end_copy) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); @@ -3980,7 +4096,7 @@ PHP_FUNCTION(pg_copy_to) PHP_PQ_ERROR("getline failed: %s", pgsql); RETURN_FALSE; } - + if (copybuf[0] == '\\' && copybuf[1] == '.' && copybuf[2] == '\0') @@ -4188,12 +4304,11 @@ PHP_FUNCTION(pg_escape_string) } to = (char *) safe_emalloc(from_len, 2, 1); - #ifdef HAVE_PQESCAPE_CONN if (pgsql_link != NULL || id != -1) { ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); to_len = (int) PQescapeStringConn(pgsql, to, from, (size_t)from_len, NULL); - } else + } else #endif to_len = (int) PQescapeString(to, from, (size_t)from_len); @@ -4268,7 +4383,7 @@ PHP_FUNCTION(pg_escape_bytea) */ static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) { - size_t buflen; + size_t buflen; unsigned char *buffer, *sp, *bp; @@ -4328,7 +4443,7 @@ static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t * memcpy(buf, sp-2, 3); buf[3] = '\0'; start = buf; - *bp = (unsigned char)strtoul(start, (char **)&end, 8); + *bp = (unsigned char)strtoul(start, (char **)&end, 8); buflen -= 3; state = 0; } @@ -4377,61 +4492,13 @@ PHP_FUNCTION(pg_unescape_bytea) #endif #ifdef HAVE_PQESCAPE -#if !HAVE_PQESCAPELITERAL -/* emulate libpq's PQescapeInternal() 9.0 or later */ -static char* php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal) { - char *result, *rp; - const char *s; - size_t tmp_len; - int input_len = len; - char quote_char = escape_literal ? '\'' : '"'; - - if (!conn) { - return NULL; - } - - /* - * NOTE: multibyte strings that could cointain slashes should be considered. - * (e.g. SJIS, BIG5) However, it cannot be done without valid PGconn and mbstring. - * Therefore, this function does not support such encodings currently. - * FIXME: add encoding check and skip multibyte char bytes if there is vaild PGconn. - */ - - /* allocate enough memory */ - rp = result = (char *)emalloc(len*2 + 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */ - - if (escape_literal) { - /* check backslashes */ - tmp_len = strspn(str, "\\"); - if (tmp_len != len) { - /* add " E" for escaping slashes */ - *rp++ = ' '; - *rp++ = 'E'; - } - } - /* open quote */ - *rp++ = quote_char; - for (s = str; s - str < input_len; ++s) { - if (*s == quote_char || (escape_literal && *s == '\\')) { - *rp++ = *s; - *rp++ = *s; - } else { - *rp++ = *s; - } - } - *rp++ = quote_char; - *rp = '\0'; - - return result; -} -#endif - static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) { char *from = NULL, *to = NULL; zval *pgsql_link = NULL; PGconn *pgsql; int from_len; int id = -1; + char *tmp; switch (ZEND_NUM_ARGS()) { case 1: @@ -4459,30 +4526,18 @@ static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_l php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot get pgsql link"); RETURN_FALSE; } -#ifdef HAVE_PQESCAPELITERAL - /* Use a block with a local var to avoid unused variable warnings */ - { - char *tmp; - if (escape_literal) { - tmp = PQescapeLiteral(pgsql, from, (size_t)from_len); - } else { - tmp = PQescapeIdentifier(pgsql, from, (size_t)from_len); - } - if (!tmp) { - php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape"); - RETURN_FALSE; - } - to = estrdup(tmp); - PQfreemem(tmp); + if (escape_literal) { + tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len); + } else { + tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len); } -#else - to = php_pgsql_PQescapeInternal(pgsql, from, (size_t)from_len, escape_literal); - if (!to) { + if (!tmp) { php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape"); RETURN_FALSE; } -#endif + to = estrdup(tmp); + PGSQLfree(tmp); RETURN_STRING(to, 0); } @@ -4530,6 +4585,7 @@ PHP_FUNCTION(pg_result_error) } /* }}} */ + #if HAVE_PQRESULTERRORFIELD /* {{{ proto string pg_result_error_field(resource result, int fieldcode) Get error message field associated with result */ @@ -4575,6 +4631,7 @@ PHP_FUNCTION(pg_result_error_field) /* }}} */ #endif + /* {{{ proto int pg_connection_status(resource connection) Get connection status */ PHP_FUNCTION(pg_connection_status) @@ -4595,6 +4652,7 @@ PHP_FUNCTION(pg_connection_status) /* }}} */ + #if HAVE_PGTRANSACTIONSTATUS /* {{{ proto int pg_transaction_status(resource connection) Get transaction status */ @@ -4617,6 +4675,7 @@ PHP_FUNCTION(pg_transaction_status) /* }}} */ + /* {{{ proto bool pg_connection_reset(resource connection) Reset connection (reconnect) */ PHP_FUNCTION(pg_connection_reset) @@ -4638,12 +4697,13 @@ PHP_FUNCTION(pg_connection_reset) } RETURN_TRUE; } - /* }}} */ + #define PHP_PG_ASYNC_IS_BUSY 1 #define PHP_PG_ASYNC_REQUEST_CANCEL 2 - + + /* {{{ php_pgsql_flush_query */ static int php_pgsql_flush_query(PGconn *pgsql TSRMLS_DC) @@ -4663,7 +4723,8 @@ static int php_pgsql_flush_query(PGconn *pgsql TSRMLS_DC) return leftover; } /* }}} */ - + + /* {{{ php_pgsql_do_async */ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) @@ -4724,6 +4785,16 @@ PHP_FUNCTION(pg_connection_busy) } /* }}} */ +static int _php_pgsql_link_has_results(PGconn *pgsql) +{ + PGresult *result; + while ((result = PQgetResult(pgsql))) { + PQclear(result); + return 1; + } + return 0; +} + /* {{{ proto bool pg_send_query(resource connection, string query) Send asynchronous query */ PHP_FUNCTION(pg_send_query) @@ -4733,48 +4804,63 @@ PHP_FUNCTION(pg_send_query) int len; int id = -1; PGconn *pgsql; - PGresult *res; - int leftover = 0; + int is_non_blocking; int ret; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", - &pgsql_link, &query, &len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &query, &len) == FAILURE) { return; } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); - if (PQ_SETNONBLOCKING(pgsql, 1)) { + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); RETURN_FALSE; } - while ((res = PQgetResult(pgsql))) { - PQclear(res); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE"); + + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); } - if (!PQsendQuery(pgsql, query)) { - if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - } + + if (is_non_blocking) { if (!PQsendQuery(pgsql, query)) { RETURN_FALSE; } - } - /* Wait to finish sending buffer */ - while ((ret = PQflush(pgsql))) { - if (ret == -1) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); - break; + ret = PQflush(pgsql); + } else { + if (!PQsendQuery(pgsql, query)) { + if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { + PQreset(pgsql); + } + if (!PQsendQuery(pgsql, query)) { + RETURN_FALSE; + } + } + + /* Wait to finish sending buffer */ + while ((ret = PQflush(pgsql))) { + if (ret == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); + break; + } + usleep(10000); + } + + if (PQ_SETNONBLOCKING(pgsql, 0)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); } - usleep(10000); } - if (PQ_SETNONBLOCKING(pgsql, 0)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); + + if (ret == 0) { + RETURN_TRUE; + } else if (ret == -1) { + RETURN_FALSE; + } else { + RETURN_LONG(0); } - RETURN_TRUE; } /* }}} */ @@ -4789,8 +4875,7 @@ PHP_FUNCTION(pg_send_query_params) char *query; int query_len, id = -1; PGconn *pgsql; - PGresult *res; - int leftover = 0; + int is_non_blocking; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa/", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) { @@ -4803,16 +4888,16 @@ PHP_FUNCTION(pg_send_query_params) ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); - if (PQ_SETNONBLOCKING(pgsql, 1)) { + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); RETURN_FALSE; } - while ((res = PQgetResult(pgsql))) { - PQclear(res); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE"); + + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); } zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr)); @@ -4848,7 +4933,12 @@ PHP_FUNCTION(pg_send_query_params) } } - if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) { + if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) { + _php_pgsql_free_params(params, num_params); + } else if (is_non_blocking) { + _php_pgsql_free_params(params, num_params); + RETURN_FALSE; + } else { if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { PQreset(pgsql); } @@ -4857,19 +4947,31 @@ PHP_FUNCTION(pg_send_query_params) RETURN_FALSE; } } - _php_pgsql_free_params(params, num_params); - /* Wait to finish sending buffer */ - while ((ret = PQflush(pgsql))) { - if (ret == -1) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); - break; + + if (is_non_blocking) { + ret = PQflush(pgsql); + } else { + /* Wait to finish sending buffer */ + while ((ret = PQflush(pgsql))) { + if (ret == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); + break; + } + usleep(10000); + } + + if (PQ_SETNONBLOCKING(pgsql, 0) != 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); } - usleep(10000); } - if (PQ_SETNONBLOCKING(pgsql, 0)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); + + if (ret == 0) { + RETURN_TRUE; + } else if (ret == -1) { + RETURN_FALSE; + } else { + RETURN_LONG(0); } - RETURN_TRUE; } /* }}} */ #endif @@ -4883,8 +4985,7 @@ PHP_FUNCTION(pg_send_prepare) char *query, *stmtname; int stmtname_len, query_len, id = -1; PGconn *pgsql; - PGresult *res; - int leftover = 0; + int is_non_blocking; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) { @@ -4893,41 +4994,58 @@ PHP_FUNCTION(pg_send_prepare) if (pgsql_link == NULL && id == -1) { RETURN_FALSE; - } + } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); - if (PQ_SETNONBLOCKING(pgsql, 1)) { + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); RETURN_FALSE; } - while ((res = PQgetResult(pgsql))) { - PQclear(res); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE"); + + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); } + if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) { - if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - } - if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) { + if (is_non_blocking) { RETURN_FALSE; + } else { + if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { + PQreset(pgsql); + } + if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) { + RETURN_FALSE; + } } } - /* Wait to finish sending buffer */ - while ((ret = PQflush(pgsql))) { - if (ret == -1) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty postgres send buffer"); - break; + + if (is_non_blocking) { + ret = PQflush(pgsql); + } else { + /* Wait to finish sending buffer */ + while ((ret = PQflush(pgsql))) { + if (ret == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); + break; + } + usleep(10000); + } + if (PQ_SETNONBLOCKING(pgsql, 0) != 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); } - usleep(10000); } - if (PQ_SETNONBLOCKING(pgsql, 0)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); + + if (ret == 0) { + RETURN_TRUE; + } else if (ret == -1) { + RETURN_FALSE; + } else { + RETURN_LONG(0); } - RETURN_TRUE; } /* }}} */ #endif @@ -4944,8 +5062,7 @@ PHP_FUNCTION(pg_send_execute) char *stmtname; int stmtname_len, id = -1; PGconn *pgsql; - PGresult *res; - int leftover = 0; + int is_non_blocking; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) { @@ -4958,16 +5075,16 @@ PHP_FUNCTION(pg_send_execute) ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); - if (PQ_SETNONBLOCKING(pgsql, 1)) { + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); RETURN_FALSE; } - while ((res = PQgetResult(pgsql))) { - PQclear(res); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE"); + + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); } zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr)); @@ -4975,8 +5092,8 @@ PHP_FUNCTION(pg_send_execute) if (num_params > 0) { int i = 0; params = (char **)safe_emalloc(sizeof(char *), num_params, 0); - - for(i = 0; i < num_params; i++) { + + for (i = 0; i < num_params; i++) { if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter"); _php_pgsql_free_params(params, num_params); @@ -5003,7 +5120,12 @@ PHP_FUNCTION(pg_send_execute) } } - if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) { + if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) { + _php_pgsql_free_params(params, num_params); + } else if (is_non_blocking) { + _php_pgsql_free_params(params, num_params); + RETURN_FALSE; + } else { if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { PQreset(pgsql); } @@ -5012,19 +5134,30 @@ PHP_FUNCTION(pg_send_execute) RETURN_FALSE; } } - _php_pgsql_free_params(params, num_params); - /* Wait to finish sending buffer */ - while ((ret = PQflush(pgsql))) { - if (ret == -1) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty postgres send buffer"); - break; + + if (is_non_blocking) { + ret = PQflush(pgsql); + } else { + /* Wait to finish sending buffer */ + while ((ret = PQflush(pgsql))) { + if (ret == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not empty PostgreSQL send buffer"); + break; + } + usleep(10000); + } + if (PQ_SETNONBLOCKING(pgsql, 0) != 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); } - usleep(10000); } - if (PQ_SETNONBLOCKING(pgsql, 0)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); + + if (ret == 0) { + RETURN_TRUE; + } else if (ret == -1) { + RETURN_FALSE; + } else { + RETURN_LONG(0); } - RETURN_TRUE; } /* }}} */ #endif @@ -5169,10 +5302,145 @@ PHP_FUNCTION(pg_get_pid) } /* }}} */ +static size_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + return 0; +} + +static size_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + return 0; +} + +static int php_pgsql_fd_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + return EOF; +} + +static int php_pgsql_fd_flush(php_stream *stream TSRMLS_DC) +{ + return FAILURE; +} + +static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + PGconn *pgsql = (PGconn *) stream->abstract; + switch (option) { + case PHP_STREAM_OPTION_BLOCKING: + return PQ_SETNONBLOCKING(pgsql, value); + default: + return FAILURE; + } +} + +static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret TSRMLS_DC) +{ + PGconn *pgsql = (PGconn *) stream->abstract; + int fd_number; + + switch (cast_as) { + case PHP_STREAM_AS_FD_FOR_SELECT: + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: + if (ret) { + fd_number = PQsocket(pgsql); + if (fd_number == -1) { + return FAILURE; + } + + *(php_socket_t *)ret = fd_number; + return SUCCESS; + } + default: + return FAILURE; + } +} + +/* {{{ proto resource pg_socket(resource) + Get a read-only handle to the socket underlying the pgsql connection */ +PHP_FUNCTION(pg_socket) +{ + zval *pgsql_link; + php_stream *stream; + PGconn *pgsql; + int id = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); + + stream = php_stream_alloc(&php_stream_pgsql_fd_ops, pgsql, NULL, "r"); + + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool pg_consume_input(resource) + Reads input on the connection */ +PHP_FUNCTION(pg_consume_input) +{ + zval *pgsql_link; + int id = -1; + PGconn *pgsql; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); + + RETURN_BOOL(PQconsumeInput(pgsql)); +} +/* }}} */ + +/* {{{ proto mixed pg_flush(resource) + Flush outbound query data on the connection */ +PHP_FUNCTION(pg_flush) +{ + zval *pgsql_link; + int id = -1; + PGconn *pgsql; + int ret; + int is_non_blocking; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); + + is_non_blocking = PQisnonblocking(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } + + ret = PQflush(pgsql); + + if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 0) == -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Failed resetting connection to blocking mode"); + } + + switch (ret) { + case 0: RETURN_TRUE; break; + case 1: RETURN_LONG(0); break; + default: RETURN_FALSE; + } +} +/* }}} */ + /* {{{ php_pgsql_meta_data * TODO: Add meta_data cache for better performance */ -PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta TSRMLS_DC) +PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended TSRMLS_DC) { PGresult *pg_result; char *src, *tmp_name, *tmp_name2 = NULL; @@ -5181,7 +5449,7 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z size_t new_len; int i, num_rows; zval *elem; - + if (!*table_name) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The table name must be specified"); return FAILURE; @@ -5196,34 +5464,41 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z tmp_name = "public"; } - smart_str_appends(&querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype = 'e' " - "FROM pg_class as c, pg_attribute a, pg_type t, pg_namespace n " - "WHERE a.attnum > 0 AND a.attrelid = c.oid AND c.relname = '"); + if (extended) { + smart_str_appends(&querystr, + "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, " + "d.description " + "FROM pg_class as c " + " JOIN pg_attribute a ON (a.attrelid = c.oid) " + " JOIN pg_type t ON (a.atttypid = t.oid) " + " JOIN pg_namespace n ON (c.relnamespace = n.oid) " + " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) " + "WHERE a.attnum > 0 AND c.relname = '"); + } else { + smart_str_appends(&querystr, + "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype " + "FROM pg_class as c " + " JOIN pg_attribute a ON (a.attrelid = c.oid) " + " JOIN pg_type t ON (a.atttypid = t.oid) " + " JOIN pg_namespace n ON (c.relnamespace = n.oid) " + "WHERE a.attnum > 0 AND c.relname = '"); + } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); -#if HAVE_PQESCAPE_CONN new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); -#else - new_len = PQescapeString(escaped, tmp_name2, strlen(tmp_name2)); -#endif if (new_len) { - smart_str_appends(&querystr, escaped); + smart_str_appendl(&querystr, escaped, new_len); } efree(escaped); - smart_str_appends(&querystr, "' AND c.relnamespace = n.oid AND n.nspname = '"); + smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); -#if HAVE_PQESCAPE_CONN new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); -#else - new_len = PQescapeString(escaped, tmp_name, strlen(tmp_name)); -#endif if (new_len) { - smart_str_appends(&querystr, escaped); + smart_str_appendl(&querystr, escaped, new_len); } efree(escaped); - smart_str_appends(&querystr, "' AND a.atttypid = t.oid ORDER BY a.attnum;"); + smart_str_appends(&querystr, "' ORDER BY a.attnum;"); smart_str_0(&querystr); efree(src); @@ -5240,28 +5515,29 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z char *name; MAKE_STD_ZVAL(elem); array_init(elem); + /* pg_attribute.attnum */ add_assoc_long(elem, "num", atoi(PQgetvalue(pg_result,i,1))); + /* pg_type.typname */ add_assoc_string(elem, "type", PQgetvalue(pg_result,i,2)); + /* pg_attribute.attlen */ add_assoc_long(elem, "len", atoi(PQgetvalue(pg_result,i,3))); - if (!strcmp(PQgetvalue(pg_result,i,4), "t")) { - add_assoc_bool(elem, "not null", 1); - } - else { - add_assoc_bool(elem, "not null", 0); - } - if (!strcmp(PQgetvalue(pg_result,i,5), "t")) { - add_assoc_bool(elem, "has default", 1); - } - else { - add_assoc_bool(elem, "has default", 0); - } + /* pg_attribute.attnonull */ + add_assoc_bool(elem, "not null", !strcmp(PQgetvalue(pg_result,i,4), "t")); + /* pg_attribute.atthasdef */ + add_assoc_bool(elem, "has default", !strcmp(PQgetvalue(pg_result,i,5), "t")); + /* pg_attribute.attndims */ add_assoc_long(elem, "array dims", atoi(PQgetvalue(pg_result,i,6))); - if (!strcmp(PQgetvalue(pg_result,i,7), "t")) { - add_assoc_bool(elem, "is enum", 1); - } - else { - add_assoc_bool(elem, "is enum", 0); - } + /* pg_type.typtype */ + add_assoc_bool(elem, "is enum", !strcmp(PQgetvalue(pg_result,i,7), "e")); + if (extended) { + /* pg_type.typtype */ + add_assoc_bool(elem, "is base", !strcmp(PQgetvalue(pg_result,i,7), "b")); + add_assoc_bool(elem, "is composite", !strcmp(PQgetvalue(pg_result,i,7), "c")); + add_assoc_bool(elem, "is pesudo", !strcmp(PQgetvalue(pg_result,i,7), "p")); + /* pg_description.description */ + add_assoc_string(elem, "description", PQgetvalue(pg_result,i,8), 1); + } + /* pg_attribute.attname */ name = PQgetvalue(pg_result,i,0); add_assoc_zval(meta, name, elem); } @@ -5273,49 +5549,40 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z /* }}} */ -/* {{{ proto array pg_meta_data(resource db, string table) +/* {{{ proto array pg_meta_data(resource db, string table [, bool extended]) Get meta_data */ PHP_FUNCTION(pg_meta_data) { zval *pgsql_link; char *table_name; uint table_name_len; + zend_bool extended=0; PGconn *pgsql; int id = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", - &pgsql_link, &table_name, &table_name_len) == FAILURE) { + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|b", + &pgsql_link, &table_name, &table_name_len, &extended) == FAILURE) { return; } ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); - + array_init(return_value); - if (php_pgsql_meta_data(pgsql, table_name, return_value TSRMLS_CC) == FAILURE) { + if (php_pgsql_meta_data(pgsql, table_name, return_value, extended TSRMLS_CC) == FAILURE) { zval_dtor(return_value); /* destroy array */ RETURN_FALSE; } - else { - HashPosition pos; - zval **val; - - for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(return_value), &pos); - zend_hash_get_current_data_ex(Z_ARRVAL_P(return_value), (void **)&val, &pos) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL_P(return_value), &pos)) { - /* delete newly added entry, in order to keep BC */ - zend_hash_del_key_or_index(Z_ARRVAL_PP(val), "is enum", sizeof("is enum"), 0, HASH_DEL_KEY); - } - } } /* }}} */ + /* {{{ php_pgsql_get_data_type */ static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len) { - /* This is stupid way to do. I'll fix it when I decied how to support + /* This is stupid way to do. I'll fix it when I decied how to support user defined types. (Yasuo) */ - + /* boolean */ if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean")) return PG_BOOL; @@ -5394,25 +5661,35 @@ static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t return PG_POLYGON; if (!strcmp(type_name, "circle")) return PG_CIRCLE; - + return PG_UNKNOWN; } /* }}} */ /* {{{ php_pgsql_convert_match - * test field value with regular expression specified. + * test field value with regular expression specified. */ -static int php_pgsql_convert_match(const char *str, const char *regex , int icase TSRMLS_DC) +static int php_pgsql_convert_match(const char *str, size_t str_len, const char *regex , int icase TSRMLS_DC) { - regex_t re; + regex_t re; regmatch_t *subs; int regopt = REG_EXTENDED; int regerr, ret = SUCCESS; + int i; + + /* Check invalid chars for POSIX regex */ + for (i = 0; i < str_len; i++) { + if (str[i] == '\n' || + str[i] == '\r' || + str[i] == '\0' ) { + return FAILURE; + } + } if (icase) { regopt |= REG_ICASE; } - + regerr = regcomp(&re, regex, regopt); if (regerr) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot compile regex"); @@ -5423,7 +5700,7 @@ static int php_pgsql_convert_match(const char *str, const char *regex , int icas regerr = regexec(&re, str, re.re_nsub+1, subs, 0); if (regerr == REG_NOMATCH) { -#ifdef PHP_DEBUG +#ifdef PHP_DEBUG php_error_docref(NULL TSRMLS_CC, E_NOTICE, "'%s' does not match with '%s'", str, regex); #endif ret = FAILURE; @@ -5445,7 +5722,7 @@ static int php_pgsql_convert_match(const char *str, const char *regex , int icas static int php_pgsql_add_quotes(zval *src, zend_bool should_free TSRMLS_DC) { smart_str str = {0}; - + assert(Z_TYPE_P(src) == IS_STRING); assert(should_free == 1 || should_free == 0); @@ -5454,7 +5731,7 @@ static int php_pgsql_add_quotes(zval *src, zend_bool should_free TSRMLS_DC) smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src)); smart_str_appendc(&str, '\''); smart_str_0(&str); - + if (should_free) { efree(Z_STRVAL_P(src)); } @@ -5478,7 +5755,7 @@ static int php_pgsql_add_quotes(zval *src, zend_bool should_free TSRMLS_DC) php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", field ); \ err = 1; \ } \ - } + } /* {{{ php_pgsql_convert * check and convert array values (fieldname=>vlaue pair) for sql @@ -5492,7 +5769,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con zval *meta, **def, **type, **not_null, **has_default, **is_enum, **val, *new_val; int key_type, err = 0, skip_field; php_pgsql_data_type data_type; - + assert(pg_link != NULL); assert(Z_TYPE_P(values) == IS_ARRAY); assert(Z_TYPE_P(result) == IS_ARRAY); @@ -5505,7 +5782,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con array_init(meta); /* table_name is escaped by php_pgsql_meta_data */ - if (php_pgsql_meta_data(pg_link, table_name, meta TSRMLS_CC) == FAILURE) { + if (php_pgsql_meta_data(pg_link, table_name, meta, 0 TSRMLS_CC) == FAILURE) { zval_dtor(meta); FREE_ZVAL(meta); return FAILURE; @@ -5515,7 +5792,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos)) { skip_field = 0; new_val = NULL; - + if ((key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &field, &field_len, &num_idx, 0, &pos)) == HASH_KEY_NON_EXISTENT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get array key type"); err = 1; @@ -5548,9 +5825,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'is enum'"); err = 1; } - if (!err && (Z_TYPE_PP(val) == IS_ARRAY || - Z_TYPE_PP(val) == IS_OBJECT || - Z_TYPE_PP(val) == IS_CONSTANT_ARRAY)) { + if (!err && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT)) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scalar values as field values"); err = 1; } @@ -5581,14 +5856,14 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con !strcmp(Z_STRVAL_PP(val), "true") || !strcmp(Z_STRVAL_PP(val), "True") || !strcmp(Z_STRVAL_PP(val), "yes") || !strcmp(Z_STRVAL_PP(val), "Yes") || !strcmp(Z_STRVAL_PP(val), "1")) { - ZVAL_STRING(new_val, "'t'", 1); + ZVAL_STRINGL(new_val, "'t'", sizeof("'t'")-1, 1); } else if (!strcmp(Z_STRVAL_PP(val), "f") || !strcmp(Z_STRVAL_PP(val), "F") || !strcmp(Z_STRVAL_PP(val), "n") || !strcmp(Z_STRVAL_PP(val), "N") || !strcmp(Z_STRVAL_PP(val), "false") || !strcmp(Z_STRVAL_PP(val), "False") || !strcmp(Z_STRVAL_PP(val), "no") || !strcmp(Z_STRVAL_PP(val), "No") || !strcmp(Z_STRVAL_PP(val), "0")) { - ZVAL_STRING(new_val, "'f'", 1); + ZVAL_STRINGL(new_val, "'f'", sizeof("'f'")-1, 1); } else { 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); @@ -5596,19 +5871,19 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } } break; - + case IS_LONG: case IS_BOOL: if (Z_LVAL_PP(val)) { - ZVAL_STRING(new_val, "'t'", 1); + ZVAL_STRINGL(new_val, "'t'", sizeof("'t'")-1, 1); } else { - ZVAL_STRING(new_val, "'f'", 1); + ZVAL_STRINGL(new_val, "'f'", sizeof("'f'")-1, 1); } break; case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5619,7 +5894,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field); } break; - + case PG_OID: case PG_INT2: case PG_INT4: @@ -5627,19 +5902,19 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con switch (Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { /* FIXME: better regex must be used */ - if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([+-]{0,1}[0-9]+)$", 0 TSRMLS_CC) == FAILURE) { + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^([+-]{0,1}[0-9]+)$", 0 TSRMLS_CC) == FAILURE) { err = 1; } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); } } break; - + case IS_DOUBLE: ZVAL_DOUBLE(new_val, Z_DVAL_PP(val)); convert_to_long_ex(&new_val); @@ -5650,7 +5925,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con break; case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5669,11 +5944,11 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con switch (Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { - /* FIXME: better regex must be used */ - 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) { + /* better regex? */ + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$", 0 TSRMLS_CC) == FAILURE) { err = 1; } else { @@ -5681,17 +5956,17 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } } break; - + case IS_LONG: ZVAL_LONG(new_val, Z_LVAL_PP(val)); break; - + case IS_DOUBLE: ZVAL_DOUBLE(new_val, Z_DVAL_PP(val)); break; - + case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5703,46 +5978,58 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } break; + /* Exotic types are handled as string also. + Please feel free to add more valitions. Invalid query fails + at execution anyway. */ case PG_TEXT: case PG_CHAR: case PG_VARCHAR: + /* bit */ + case PG_BIT: + case PG_VARBIT: + /* geometric */ + case PG_LINE: + case PG_LSEG: + case PG_POINT: + case PG_BOX: + case PG_PATH: + case PG_POLYGON: + case PG_CIRCLE: + /* unknown. JSON, Array etc */ + case PG_UNKNOWN: switch (Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { if (opt & PGSQL_CONV_FORCE_NULL) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { - ZVAL_STRING(new_val, "''", 1); + ZVAL_STRINGL(new_val, "''", sizeof("''")-1, 1); } } else { + char *tmp; + /* PostgreSQL ignores \0 */ Z_TYPE_P(new_val) = IS_STRING; -#if HAVE_PQESCAPE_CONN - { - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1); - Z_STRLEN_P(new_val) = (int)PQescapeStringConn(pg_link, tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val), NULL); - Z_STRVAL_P(new_val) = tmp; - } -#else - Z_STRVAL_P(new_val) = (int)PQescapeString(Z_STRVAL_PP(val), Z_STRLEN_PP(val), &Z_STRLEN_P(new_val), 0 TSRMLS_CC); -#endif + tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1); + /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ + Z_STRLEN_P(new_val) = (int)PQescapeStringConn(pg_link, tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val), NULL); + Z_STRVAL_P(new_val) = tmp; php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } break; - + case IS_LONG: ZVAL_LONG(new_val, Z_LVAL_PP(val)); convert_to_string_ex(&new_val); break; - + case IS_DOUBLE: ZVAL_DOUBLE(new_val, Z_DVAL_PP(val)); convert_to_string_ex(&new_val); break; case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5753,38 +6040,38 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field); } break; - + case PG_UNIX_TIME: case PG_UNIX_TIME_INTERVAL: /* these are the actallay a integer */ switch (Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { - /* FIXME: Better regex must be used */ - if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^[0-9]+$", 0 TSRMLS_CC) == FAILURE) { + /* better regex? */ + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^[0-9]+$", 0 TSRMLS_CC) == FAILURE) { err = 1; - } + } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); convert_to_long_ex(&new_val); } } break; - + case IS_DOUBLE: ZVAL_DOUBLE(new_val, Z_DVAL_PP(val)); convert_to_long_ex(&new_val); break; - + case IS_LONG: ZVAL_LONG(new_val, Z_LVAL_PP(val)); break; - + case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5795,39 +6082,39 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_PP(type), field); } break; - + case PG_CIDR: case PG_INET: switch (Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { - /* FIXME: Better regex must be used */ - 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) { + /* better regex? IPV6 and IPV4 */ + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$", 0 TSRMLS_CC) == FAILURE) { err = 1; } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } } break; case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: err = 1; - } + } PGSQL_CONV_CHECK_IGNORE(); if (err) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for '%s' (%s)", Z_STRVAL_PP(type), field); } break; - + case PG_TIME_WITH_TIMEZONE: case PG_TIMESTAMP: case PG_TIMESTAMP_WITH_TIMEZONE: @@ -5838,8 +6125,8 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } else if (!strcasecmp(Z_STRVAL_PP(val), "now()")) { ZVAL_STRINGL(new_val, "NOW()", sizeof("NOW()")-1, 1); } else { - /* FIXME: better regex must be used */ - 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) { + /* better regex? */ + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})([ \\t]+(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$", 1 TSRMLS_CC) == FAILURE) { err = 1; } else { ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); @@ -5847,7 +6134,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } } break; - + case IS_NULL: ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; @@ -5860,27 +6147,27 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field); } break; - + case PG_DATE: switch(Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { /* FIXME: better regex must be used */ - if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$", 1 TSRMLS_CC) == FAILURE) { + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$", 1 TSRMLS_CC) == FAILURE) { err = 1; } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } } break; - + case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5896,22 +6183,22 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con switch(Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { /* FIXME: better regex must be used */ - 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) { + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1})){0,1}$", 1 TSRMLS_CC) == FAILURE) { err = 1; } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } } break; - + case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -5946,10 +6233,10 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con Quantities of days, hours, minutes, and seconds can be specified without explicit unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10 sec'. - */ - if (php_pgsql_convert_match(Z_STRVAL_PP(val), + */ + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^(@?[ \\t]+)?(" - + /* Textual time units and their abbreviations: */ "(([-+]?[ \\t]+)?" "[0-9]+(\\.[0-9]*)?[ \\t]*" @@ -5986,9 +6273,9 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } - } + } break; - + case IS_NULL: ZVAL_STRING(new_val, "NULL", 1); break; @@ -6030,19 +6317,19 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con Z_STRLEN_P(new_val) = s.len; } break; - + case IS_LONG: ZVAL_LONG(new_val, Z_LVAL_PP(val)); convert_to_string_ex(&new_val); break; - + case IS_DOUBLE: ZVAL_DOUBLE(new_val, Z_DVAL_PP(val)); convert_to_string_ex(&new_val); break; case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -6059,21 +6346,21 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con switch(Z_TYPE_PP(val)) { case IS_STRING: if (Z_STRLEN_PP(val) == 0) { - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); } else { - 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) { + if (php_pgsql_convert_match(Z_STRVAL_PP(val), Z_STRLEN_PP(val), "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$", 1 TSRMLS_CC) == FAILURE) { err = 1; } else { - ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1); + ZVAL_STRINGL(new_val, Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); php_pgsql_add_quotes(new_val, 1 TSRMLS_CC); } } break; - + case IS_NULL: - ZVAL_STRING(new_val, "NULL", 1); + ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1); break; default: @@ -6085,28 +6372,13 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con } break; - /* bit */ - case PG_BIT: - case PG_VARBIT: - /* geometric */ - case PG_LINE: - case PG_LSEG: - case PG_POINT: - case PG_BOX: - case PG_PATH: - case PG_POLYGON: - case PG_CIRCLE: - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "PostgreSQL '%s' type (%s) is not supported", Z_STRVAL_PP(type), field); - err = 1; - break; - - case PG_UNKNOWN: default: - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown or system data type '%s' for '%s'", Z_STRVAL_PP(type), field); + /* should not happen */ + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_PP(type), field); err = 1; break; } /* switch */ - + if (err) { zval_dtor(new_val); FREE_ZVAL(new_val); @@ -6118,16 +6390,12 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con size_t field_len = strlen(field); if (_php_pgsql_detect_identifier_escape(field, field_len) == SUCCESS) { - escaped = _php_pgsql_strndup(field, field_len); + add_assoc_zval(result, field, new_val); } else { -#if HAVE_PQESCAPELITERAL - escaped = PQescapeIdentifier(pg_link, field, field_len); -#else - escaped = _php_pgsql_escape_identifier(field, field_len); -#endif + escaped = PGSQLescapeIdentifier(pg_link, field, field_len); + add_assoc_zval(result, escaped, new_val); + PGSQLfree(escaped); } - add_assoc_zval(result, escaped, new_val); - free(escaped); } } /* for */ zval_dtor(meta); @@ -6152,7 +6420,7 @@ PHP_FUNCTION(pg_convert) ulong option = 0; PGconn *pg_link; int id = -1; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) { return; @@ -6212,31 +6480,24 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const c token = php_strtok_r(table_copy, ".", &tmp); len = strlen(token); if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) { - escaped = _php_pgsql_strndup(token, len); + smart_str_appendl(querystr, token, len); } else { -#if HAVE_PQESCAPELITERAL - escaped = PQescapeIdentifier(pg_link, token, len); -#else - escaped = _php_pgsql_escape_identifier(token, len); -#endif + escaped = PGSQLescapeIdentifier(pg_link, token, len); + smart_str_appends(querystr, escaped); + PGSQLfree(escaped); } - smart_str_appends(querystr, escaped); - free(escaped); if (tmp && *tmp) { len = strlen(tmp); /* "schema"."table" format */ if (_php_pgsql_detect_identifier_escape(tmp, len) == SUCCESS) { - escaped = _php_pgsql_strndup(tmp, len); + smart_str_appendc(querystr, '.'); + smart_str_appendl(querystr, tmp, len); } else { -#if HAVE_PQESCAPELITERAL - escaped = PQescapeIdentifier(pg_link, tmp, len); -#else - escaped = _php_pgsql_escape_identifier(tmp, len); -#endif + escaped = PGSQLescapeIdentifier(pg_link, tmp, len); + smart_str_appendc(querystr, '.'); + smart_str_appends(querystr, escaped); + PGSQLfree(escaped); } - smart_str_appendc(querystr, '.'); - smart_str_appends(querystr, escaped); - free(escaped); } efree(table_copy); } @@ -6247,7 +6508,7 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var { zval **val, *converted = NULL; char buf[256]; - char *fld; + char *fld, *tmp; smart_str querystr = {0}; int key_type, ret = FAILURE; uint fld_len; @@ -6267,7 +6528,7 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var } /* convert input array if needed */ - if (!(opt & PGSQL_DML_NO_CONV)) { + if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) { MAKE_STD_ZVAL(converted); array_init(converted); if (php_pgsql_convert(pg_link, table, var_array, converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) { @@ -6287,7 +6548,13 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects associative array for values to be inserted"); goto cleanup; } - smart_str_appendl(&querystr, fld, fld_len - 1); + if (opt & PGSQL_DML_ESCAPE) { + tmp = PGSQLescapeIdentifier(pg_link, fld, fld_len); + smart_str_appends(&querystr, tmp); + PGSQLfree(tmp); + } else { + smart_str_appendl(&querystr, fld, fld_len - 1); + } smart_str_appendc(&querystr, ','); zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos); } @@ -6302,7 +6569,18 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var /* we can avoid the key_type check here, because we tested it in the other loop */ switch(Z_TYPE_PP(val)) { case IS_STRING: - smart_str_appendl(&querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + if (opt & PGSQL_DML_ESCAPE) { + size_t new_len; + char *tmp; + tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1); + new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val), NULL); + smart_str_appendc(&querystr, '\''); + smart_str_appendl(&querystr, tmp, new_len); + smart_str_appendc(&querystr, '\''); + efree(tmp); + } else { + smart_str_appendl(&querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } break; case IS_LONG: smart_str_append_long(&querystr, Z_LVAL_PP(val)); @@ -6310,9 +6588,11 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var case IS_DOUBLE: smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_PP(val))); break; + case IS_NULL: + smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1); + break; default: - /* should not happen */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Report this error to php-dev@lists.php.net, type = %d", Z_TYPE_PP(val)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expects scaler values. type = %d", Z_TYPE_PP(val)); goto cleanup; break; } @@ -6336,7 +6616,7 @@ no_values: cleanup: if (!(opt & PGSQL_DML_NO_CONV) && converted) { - zval_dtor(converted); + zval_dtor(converted); FREE_ZVAL(converted); } if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) { @@ -6356,15 +6636,18 @@ PHP_FUNCTION(pg_insert) zval *pgsql_link, *values; char *table, *sql = NULL; int table_len; - ulong option = PGSQL_DML_EXEC; + ulong option = PGSQL_DML_EXEC, return_sql; PGconn *pg_link; + PGresult *pg_result; + ExecStatusType status; + pgsql_result_handle *pgsql_handle; int id = -1, argc = ZEND_NUM_ARGS(); if (zend_parse_parameters(argc TSRMLS_CC, "rsa|l", &pgsql_link, &table, &table_len, &values, &option) == FAILURE) { return; } - if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING)) { + if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified"); RETURN_FALSE; } @@ -6374,44 +6657,105 @@ PHP_FUNCTION(pg_insert) if (php_pgsql_flush_query(pg_link TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection"); } - if (php_pgsql_insert(pg_link, table, values, option, &sql TSRMLS_CC) == FAILURE) { + return_sql = option & PGSQL_DML_STRING; + if (option & PGSQL_DML_EXEC) { + /* return resource when executed */ + option = option & ~PGSQL_DML_EXEC; + if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + pg_result = PQexec(pg_link, sql); + if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) { + PQclear(pg_result); + PQreset(pg_link); + pg_result = PQexec(pg_link, sql); + } + efree(sql); + + if (pg_result) { + status = PQresultStatus(pg_result); + } else { + status = (ExecStatusType) PQstatus(pg_link); + } + + switch (status) { + case PGRES_EMPTY_QUERY: + case PGRES_BAD_RESPONSE: + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + PHP_PQ_ERROR("Query failed: %s", pg_link); + PQclear(pg_result); + RETURN_FALSE; + break; + case PGRES_COMMAND_OK: /* successful command that did not return rows */ + default: + if (pg_result) { + pgsql_handle = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle)); + pgsql_handle->conn = pg_link; + pgsql_handle->result = pg_result; + pgsql_handle->row = 0; + ZEND_REGISTER_RESOURCE(return_value, pgsql_handle, le_result); + return; + } else { + PQclear(pg_result); + RETURN_FALSE; + } + break; + } + } else if (php_pgsql_insert(pg_link, table, values, option, &sql TSRMLS_CC) == FAILURE) { RETURN_FALSE; } - if (option & PGSQL_DML_STRING) { + if (return_sql) { RETURN_STRING(sql, 0); } RETURN_TRUE; } /* }}} */ -static inline int build_assignment_string(smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len TSRMLS_DC) +static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len, ulong opt TSRMLS_DC) { HashPosition pos; uint fld_len; int key_type; ulong num_idx; - char *fld; + char *fld, *tmp; char buf[256]; zval **val; for (zend_hash_internal_pointer_reset_ex(ht, &pos); zend_hash_get_current_data_ex(ht, (void **)&val, &pos) == SUCCESS; zend_hash_move_forward_ex(ht, &pos)) { - key_type = zend_hash_get_current_key_ex(ht, &fld, &fld_len, &num_idx, 0, &pos); + key_type = zend_hash_get_current_key_ex(ht, &fld, &fld_len, &num_idx, 0, &pos); if (key_type == HASH_KEY_IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects associative array for values to be inserted"); return -1; } - smart_str_appendl(querystr, fld, fld_len - 1); - if (where_cond && Z_TYPE_PP(val) == IS_STRING && !strcmp(Z_STRVAL_PP(val), "NULL")) { + if (opt & PGSQL_DML_ESCAPE) { + tmp = PGSQLescapeIdentifier(pg_link, fld, fld_len); + smart_str_appends(querystr, tmp); + PGSQLfree(tmp); + } else { + smart_str_appendl(querystr, fld, fld_len - 1); + } + if (where_cond && (Z_TYPE_PP(val) == IS_BOOL || (Z_TYPE_PP(val) == IS_STRING && !strcmp(Z_STRVAL_PP(val), "NULL")))) { smart_str_appends(querystr, " IS "); } else { smart_str_appendc(querystr, '='); } - + switch(Z_TYPE_PP(val)) { case IS_STRING: - smart_str_appendl(querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + if (opt & PGSQL_DML_ESCAPE) { + size_t new_len; + tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1); + new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val), NULL); + smart_str_appendc(querystr, '\''); + smart_str_appendl(querystr, tmp, new_len); + smart_str_appendc(querystr, '\''); + efree(tmp); + } else { + smart_str_appendl(querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } break; case IS_LONG: smart_str_append_long(querystr, Z_LVAL_PP(val)); @@ -6419,9 +6763,11 @@ static inline int build_assignment_string(smart_str *querystr, HashTable *ht, in case IS_DOUBLE: smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_PP(val)), sizeof(buf)-1)); break; + case IS_NULL: + smart_str_appendl(querystr, "NULL", sizeof("NULL")-1); + break; default: - /* should not happen */ - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scaler values other than NULL. Need to convert?"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expects scaler values. type=%d", Z_TYPE_PP(val)); return -1; } smart_str_appendl(querystr, pad, pad_len); @@ -6443,14 +6789,14 @@ PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var assert(table != NULL); assert(Z_TYPE_P(var_array) == IS_ARRAY); assert(Z_TYPE_P(ids_array) == IS_ARRAY); - assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING))); + assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE))); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0 || zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) { return FAILURE; } - if (!(opt & PGSQL_DML_NO_CONV)) { + if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) { MAKE_STD_ZVAL(var_converted); array_init(var_converted); if (php_pgsql_convert(pg_link, table, var_array, var_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) { @@ -6469,15 +6815,15 @@ PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var build_tablename(&querystr, pg_link, table); smart_str_appends(&querystr, " SET "); - if (build_assignment_string(&querystr, Z_ARRVAL_P(var_array), 0, ",", 1 TSRMLS_CC)) + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt TSRMLS_CC)) goto cleanup; smart_str_appends(&querystr, " WHERE "); - if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC)) + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt TSRMLS_CC)) goto cleanup; - smart_str_appendc(&querystr, ';'); + smart_str_appendc(&querystr, ';'); smart_str_0(&querystr); if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt TSRMLS_CC) == 0) { @@ -6520,7 +6866,7 @@ PHP_FUNCTION(pg_update) &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) { return; } - if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING)) { + if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified"); RETURN_FALSE; } @@ -6537,7 +6883,7 @@ PHP_FUNCTION(pg_update) RETURN_STRING(sql, 0); } RETURN_TRUE; -} +} /* }}} */ /* {{{ php_pgsql_delete @@ -6551,13 +6897,13 @@ PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids assert(pg_link != NULL); assert(table != NULL); assert(Z_TYPE_P(ids_array) == IS_ARRAY); - assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING))); - + assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE))); + if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) { return FAILURE; } - if (!(opt & PGSQL_DML_NO_CONV)) { + if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) { MAKE_STD_ZVAL(ids_converted); array_init(ids_converted); if (php_pgsql_convert(pg_link, table, ids_array, ids_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) { @@ -6570,7 +6916,7 @@ PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids build_tablename(&querystr, pg_link, table); smart_str_appends(&querystr, " WHERE "); - if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC)) + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt TSRMLS_CC)) goto cleanup; smart_str_appendc(&querystr, ';'); @@ -6583,8 +6929,8 @@ PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids } cleanup: - if (!(opt & PGSQL_DML_NO_CONV)) { - zval_dtor(ids_converted); + if (ids_converted) { + zval_dtor(ids_converted); FREE_ZVAL(ids_converted); } if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) { @@ -6612,7 +6958,7 @@ PHP_FUNCTION(pg_delete) &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) { return; } - if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING)) { + if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified"); RETURN_FALSE; } @@ -6671,7 +7017,7 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array TS /* {{{ php_pgsql_select */ -PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, ulong opt, char **sql TSRMLS_DC) +PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, ulong opt, char **sql TSRMLS_DC) { zval *ids_converted = NULL; smart_str querystr = {0}; @@ -6682,13 +7028,13 @@ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids assert(table != NULL); assert(Z_TYPE_P(ids_array) == IS_ARRAY); assert(Z_TYPE_P(ret_array) == IS_ARRAY); - assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING))); - + assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE))); + if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) { return FAILURE; } - if (!(opt & PGSQL_DML_NO_CONV)) { + if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) { MAKE_STD_ZVAL(ids_converted); array_init(ids_converted); if (php_pgsql_convert(pg_link, table, ids_array, ids_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) { @@ -6701,7 +7047,7 @@ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids build_tablename(&querystr, pg_link, table); smart_str_appends(&querystr, " WHERE "); - if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC)) + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt TSRMLS_CC)) goto cleanup; smart_str_appendc(&querystr, ';'); @@ -6716,8 +7062,8 @@ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids PQclear(pg_result); cleanup: - if (!(opt & PGSQL_DML_NO_CONV)) { - zval_dtor(ids_converted); + if (ids_converted) { + zval_dtor(ids_converted); FREE_ZVAL(ids_converted); } if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) { @@ -6745,7 +7091,7 @@ PHP_FUNCTION(pg_select) &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) { return; } - if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING)) { + if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified"); RETURN_FALSE; } @@ -6765,7 +7111,7 @@ PHP_FUNCTION(pg_select) RETURN_STRING(sql, 0); } return; -} +} /* }}} */ #endif |