From: Yasuo Ohgaki Date: Sun, 16 Feb 2014 03:22:52 +0000 (+0900) Subject: EXPERIMENTAL flags for pg_select/pg_insert/pg_update/pg_delete are removed. X-Git-Tag: php-5.6.0alpha3~1^2~83 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=56854511d8f046d49cd34666764b37698d961431;p=php EXPERIMENTAL flags for pg_select/pg_insert/pg_update/pg_delete are removed. Use string escape for exotic types that allows to handle any data types. i.e. Array, JSON, JSONB, etc will work. Add escape only query for better performance which removes meta data look up. Limitations forced by pg_convert() can be avoided with this. PGSQL_DML_ESCAPE constant is added for it. --- diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index f0f8a73f76..a4d2d4a0e0 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1156,6 +1156,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); @@ -5583,14 +5584,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); @@ -5602,15 +5603,15 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con 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: @@ -5629,7 +5630,7 @@ 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 */ @@ -5637,7 +5638,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con 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; @@ -5652,7 +5653,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: @@ -5671,11 +5672,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), Z_STRLEN_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 { @@ -5693,7 +5694,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: @@ -5705,22 +5706,40 @@ 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; 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); @@ -5738,7 +5757,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: @@ -5756,15 +5775,15 @@ 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 */ + /* 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); } } @@ -5780,7 +5799,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: @@ -5797,22 +5816,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), Z_STRLEN_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: @@ -5834,7 +5853,7 @@ 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 */ + /* 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 { @@ -5861,7 +5880,7 @@ 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 */ @@ -5869,14 +5888,14 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con 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: @@ -5892,7 +5911,7 @@ 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 */ @@ -5900,14 +5919,14 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con 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: @@ -6038,7 +6057,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: @@ -6055,21 +6074,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), 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: @@ -6081,24 +6100,9 @@ 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 */ @@ -6232,7 +6236,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; @@ -6252,7 +6256,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) { @@ -6272,7 +6276,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); } @@ -6287,7 +6297,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)); @@ -6295,9 +6316,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; } @@ -6349,7 +6372,7 @@ PHP_FUNCTION(pg_insert) &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; } @@ -6369,13 +6392,13 @@ PHP_FUNCTION(pg_insert) } /* }}} */ -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; @@ -6387,8 +6410,14 @@ static inline int build_assignment_string(smart_str *querystr, HashTable *ht, in 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, '='); @@ -6396,7 +6425,17 @@ static inline int build_assignment_string(smart_str *querystr, HashTable *ht, in 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)); @@ -6404,9 +6443,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); @@ -6428,14 +6469,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) { @@ -6454,15 +6495,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) { @@ -6505,7 +6546,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; } @@ -6536,13 +6577,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) { @@ -6555,7 +6596,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, ';'); @@ -6568,7 +6609,7 @@ PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids } cleanup: - if (!(opt & PGSQL_DML_NO_CONV)) { + if (ids_converted) { zval_dtor(ids_converted); FREE_ZVAL(ids_converted); } @@ -6597,7 +6638,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; } @@ -6661,7 +6702,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}; @@ -6672,13 +6713,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) { @@ -6691,7 +6732,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, ';'); @@ -6706,7 +6747,7 @@ 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)) { + if (ids_converted) { zval_dtor(ids_converted); FREE_ZVAL(ids_converted); } @@ -6735,7 +6776,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; } diff --git a/ext/pgsql/php_pgsql.h b/ext/pgsql/php_pgsql.h index 46b440d723..4e0eca808a 100644 --- a/ext/pgsql/php_pgsql.h +++ b/ext/pgsql/php_pgsql.h @@ -199,6 +199,7 @@ PHP_FUNCTION(pg_select); #define PGSQL_DML_EXEC (1<<9) /* Execute query */ #define PGSQL_DML_ASYNC (1<<10) /* Do async query */ #define PGSQL_DML_STRING (1<<11) /* Return query string */ +#define PGSQL_DML_ESCAPE (1<<12) /* No convert, but escape only */ /* exported functions */ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta TSRMLS_DC); diff --git a/ext/pgsql/tests/01createdb.phpt b/ext/pgsql/tests/01createdb.phpt index 15849396c6..629f7c979d 100644 --- a/ext/pgsql/tests/01createdb.phpt +++ b/ext/pgsql/tests/01createdb.phpt @@ -4,14 +4,14 @@ PostgreSQL create db --FILE-- +--FILE-- +'{"meeting", "lunch", "training", "presentation"}', + 'jsn'=>'{"f1":1,"f2":"foo"}', +); +$converted = pg_convert($db, $table_name_92, $fields); +var_dump($converted); + +if (!pg_insert($db, $table_name_92, $fields)) { + echo "Error\n"; +} else { + echo "OK\n"; +} + +?> +--EXPECT-- +array(2) { + [""textary""]=> + string(51) "E'{"meeting", "lunch", "training", "presentation"}'" + [""jsn""]=> + string(22) "E'{"f1":1,"f2":"foo"}'" +} +OK diff --git a/ext/pgsql/tests/12pg_insert_9.phpt b/ext/pgsql/tests/12pg_insert_9.phpt index 329364ad64..bedf3e29ad 100644 --- a/ext/pgsql/tests/12pg_insert_9.phpt +++ b/ext/pgsql/tests/12pg_insert_9.phpt @@ -18,9 +18,11 @@ $fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB'); pg_insert($db, $table_name, $fields) or print "Error in test 1\n"; echo pg_insert($db, $table_name, $fields, PGSQL_DML_STRING)."\n"; +echo pg_insert($db, $table_name, $fields, PGSQL_DML_STRING|PGSQL_DML_ESCAPE)."\n"; echo "Ok\n"; ?> --EXPECT-- INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,E'AAA',E'\\x424242'); +INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES ('1234','AAA','BBB'); Ok \ No newline at end of file diff --git a/ext/pgsql/tests/13pg_select_9.phpt b/ext/pgsql/tests/13pg_select_9.phpt index 67adc9d21d..73582b650b 100644 --- a/ext/pgsql/tests/13pg_select_9.phpt +++ b/ext/pgsql/tests/13pg_select_9.phpt @@ -20,6 +20,7 @@ $ids = array('num'=>'1234'); $res = pg_select($db, $table_name, $ids) or print "Error\n"; var_dump($res); echo pg_select($db, $table_name, $ids, PGSQL_DML_STRING)."\n"; +echo pg_select($db, $table_name, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAPE)."\n"; echo "Ok\n"; ?> @@ -36,4 +37,5 @@ array(1) { } } SELECT * FROM "php_pgsql_test" WHERE "num"=1234; +SELECT * FROM "php_pgsql_test" WHERE "num"='1234'; Ok \ No newline at end of file diff --git a/ext/pgsql/tests/14pg_update.phpt b/ext/pgsql/tests/14pg_update.phpt index 347cac9447..d804574405 100644 --- a/ext/pgsql/tests/14pg_update.phpt +++ b/ext/pgsql/tests/14pg_update.phpt @@ -17,6 +17,7 @@ $ids = array('num'=>'1234'); pg_update($db, $table_name, $fields, $ids) or print "Error in test 1\n"; echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING)."\n"; +echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAPE)."\n"; echo "Ok\n"; ?> diff --git a/ext/pgsql/tests/14pg_update_9.phpt b/ext/pgsql/tests/14pg_update_9.phpt index c33f1afbd6..9f1b516b9d 100644 --- a/ext/pgsql/tests/14pg_update_9.phpt +++ b/ext/pgsql/tests/14pg_update_9.phpt @@ -19,9 +19,11 @@ $ids = array('num'=>'1234'); pg_update($db, $table_name, $fields, $ids) or print "Error in test 1\n"; echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING)."\n"; +echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAPE)."\n"; echo "Ok\n"; ?> --EXPECT-- UPDATE "php_pgsql_test" SET "num"=1234,"str"=E'ABC',"bin"=E'\\x58595a' WHERE "num"=1234; +UPDATE "php_pgsql_test" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234'; Ok \ No newline at end of file diff --git a/ext/pgsql/tests/15pg_delete.phpt b/ext/pgsql/tests/15pg_delete.phpt index e35f4ba78e..3b9f7d803c 100644 --- a/ext/pgsql/tests/15pg_delete.phpt +++ b/ext/pgsql/tests/15pg_delete.phpt @@ -12,6 +12,8 @@ $db = pg_connect($conn_str); $fields = array('num'=>'1234', 'str'=>'XXX', 'bin'=>'YYY'); $ids = array('num'=>'1234'); +echo pg_delete($db, $table_name, $ids, PGSQL_DML_STRING)."\n"; +echo pg_delete($db, $table_name, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAPE)."\n"; if (!pg_delete($db, $table_name, $ids)) { echo "Error\n"; } @@ -20,4 +22,6 @@ else { } ?> --EXPECT-- +DELETE FROM "php_pgsql_test" WHERE "num"=1234; +DELETE FROM "php_pgsql_test" WHERE "num"='1234'; Ok diff --git a/ext/pgsql/tests/9999dropdb.phpt b/ext/pgsql/tests/9999dropdb.phpt index c60eeda8d6..8cb178b2bf 100644 --- a/ext/pgsql/tests/9999dropdb.phpt +++ b/ext/pgsql/tests/9999dropdb.phpt @@ -10,6 +10,7 @@ include('config.inc'); $db = pg_connect($conn_str); pg_query($db, "DROP TABLE ".$table_name); +@pg_query($db, "DROP TABLE ".$table_name_92); echo "OK"; diff --git a/ext/pgsql/tests/config.inc b/ext/pgsql/tests/config.inc index ffe31a875e..d363d943f6 100644 --- a/ext/pgsql/tests/config.inc +++ b/ext/pgsql/tests/config.inc @@ -4,11 +4,13 @@ // "test" database must be existed. i.e. "createdb test" before testing // PostgreSQL uses login name as username, user must have access to "test" database. -$conn_str = "host=localhost dbname=test port=5432"; // connection string -$table_name = "php_pgsql_test"; // test table that will be created -$num_test_record = 1000; // Number of records to create +$conn_str = "host=localhost dbname=test port=5432"; // connection string +$table_name = "php_pgsql_test"; // test table that will be created +$table_name_92 = "php_pgsql_test_92"; // test table that will be created +$num_test_record = 1000; // Number of records to create -$table_def = "CREATE TABLE php_pgsql_test (num int, str text, bin bytea);"; // Test table +// Test table +$table_def = "CREATE TABLE ${table_name} (num int, str text, bin bytea);"; +$table_def_92 = "CREATE TABLE ${table_name_92} (textary text[], jsn json);"; $field_name = "num"; // For pg_field_num() -?> \ No newline at end of file