Hi all Current PostgreSQL has new escape functions for literals and identifiers. Since there is no function for that, I made patch for that. Attached patch is for trunk and tested with PostgreSQL 8.4 and 9.0.
This patch would also applied to 5.4 branch but how about 5.3? It's pain not to have escape functions. Any comments? -- Yasuo Ohgaki yohg...@ohgaki.net
Index: ext/pgsql/tests/08escape.phpt =================================================================== --- ext/pgsql/tests/08escape.phpt (リビジョン 311245) +++ ext/pgsql/tests/08escape.phpt (作業コピー) @@ -11,8 +11,9 @@ // pg_escape_string() test $before = "ABC\\ABC\'"; $expect = "ABC\\\\ABC\\'"; +$expect2 = "ABC\\\\ABC\\\\''"; //the way escape string differs from PostgreSQL 9.0 $after = pg_escape_string($before); -if ($expect === $after) { +if ($expect === $after || $expect2 === $after) { echo "pg_escape_string() is Ok\n"; } else { @@ -58,11 +59,38 @@ echo "pg_escape_bytea() is broken\n"; } +// pg_escape_literal/pg_escape_identifier +$before = "ABC\\ABC\'"; +$expect = " E'ABC\\\\ABC\\\\'''"; +$after = pg_escape_literal($before); +if ($expect === $after) { + echo "pg_escape_literal() is Ok\n"; +} +else { + echo "pg_escape_literal() is NOT Ok\n"; + var_dump($before); + var_dump($after); + var_dump($expect); +} + +$before = "ABC\\ABC\'"; +$expect = "\"ABC\ABC\'\""; +$after = pg_escape_identifier($before); +if ($expect === $after) { + echo "pg_escape_identifier() is Ok\n"; +} +else { + echo "pg_escape_identifier() is NOT Ok\n"; + var_dump($before); + var_dump($after); + var_dump($expect); +} + + ?> --EXPECT-- -pg_escape_string() is NOT Ok -string(9) "ABC\ABC\'" -string(12) "ABC\\ABC\\''" -string(10) "ABC\\ABC\'" +pg_escape_string() is Ok pg_escape_bytea() is Ok pg_escape_bytea() actually works with database +pg_escape_literal() is Ok +pg_escape_identifier() is Ok \ No newline at end of file Index: ext/pgsql/php_pgsql.h =================================================================== --- ext/pgsql/php_pgsql.h (リビジョン 311245) +++ ext/pgsql/php_pgsql.h (作業コピー) @@ -173,6 +173,8 @@ PHP_FUNCTION(pg_escape_bytea); PHP_FUNCTION(pg_unescape_bytea); #endif +PHP_FUNCTION(pg_escape_literal); +PHP_FUNCTION(pg_escape_identifier); /* misc functions */ PHP_FUNCTION(pg_meta_data); Index: ext/pgsql/config.m4 =================================================================== --- ext/pgsql/config.m4 (リビジョン 311245) +++ ext/pgsql/config.m4 (作業コピー) @@ -94,6 +94,7 @@ AC_CHECK_LIB(pq, pg_encoding_to_char,AC_DEFINE(HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT,1,[Whether libpq is compiled with --enable-multibyte])) AC_CHECK_LIB(pq, lo_create, AC_DEFINE(HAVE_PG_LO_CREATE,1,[PostgreSQL 8.1 or later])) AC_CHECK_LIB(pq, lo_import_with_oid, AC_DEFINE(HAVE_PG_LO_IMPORT_WITH_OID,1,[PostgreSQL 8.4 or later])) + AC_CHECK_LIB(pq, PQescapeLiteral, AC_DEFINE(HAVE_PQESCAPELITERAL,1,[PostgreSQL 9.0 or later])) LIBS=$old_LIBS LDFLAGS=$old_LDFLAGS Index: ext/pgsql/pgsql.c =================================================================== --- ext/pgsql/pgsql.c (リビジョン 311245) +++ ext/pgsql/pgsql.c (作業コピー) @@ -422,6 +422,17 @@ ZEND_END_ARG_INFO() #endif +#if HAVE_PQESCAPE +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0) + ZEND_ARG_INFO(0, connection) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0) + ZEND_ARG_INFO(0, connection) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1) ZEND_ARG_INFO(0, result) ZEND_END_ARG_INFO() @@ -653,6 +664,8 @@ PHP_FE(pg_escape_bytea, arginfo_pg_escape_bytea) PHP_FE(pg_unescape_bytea, arginfo_pg_unescape_bytea) #endif + PHP_FE(pg_escape_literal, arginfo_pg_escape_literal) + PHP_FE(pg_escape_identifier, arginfo_pg_escape_identifier) #if HAVE_PQSETERRORVERBOSITY PHP_FE(pg_set_error_verbosity, arginfo_pg_set_error_verbosity) #endif @@ -815,7 +828,7 @@ TSRMLS_FETCH(); if (! PGG(ignore_notices)) { notice = (php_pgsql_notice *)emalloc(sizeof(php_pgsql_notice)); - notice->message = _php_pgsql_trim_message(message, ¬ice->len); + notice->message = _php_pgsql_trim_message(message, (int *)¬ice->len); if (PGG(log_notices)) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s", notice->message); } @@ -4200,6 +4213,128 @@ /* }}} */ #endif +#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, *tmp = NULL; + zval *pgsql_link = NULL; + PGconn *pgsql; + int to_len; + int from_len; + int id = -1; + + switch (ZEND_NUM_ARGS()) { + case 1: + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) { + return; + } + pgsql_link = NULL; + id = PGG(default_link); + break; + + default: + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) { + return; + } + break; + } + + if (pgsql_link == NULL && id == -1) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink); + if (pgsql == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot get default pgsql link"); + RETURN_FALSE; + } +#ifdef HAVE_PQESCAPELITERAL + 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); +#else + to = php_pgsql_PQescapeInternal(pgsql, from, (size_t)from_len, escape_literal); + if (!to) { + php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape"); + RETURN_FALSE; + } +#endif + + RETURN_STRING(to, 0); +} + +/* {{{ proto string pg_escape_literal([resource connection,] string data) + Escape parameter as string literal (i.e. parameter) */ +PHP_FUNCTION(pg_escape_literal) +{ + php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto string pg_escape_identifier([resource connection,] string data) + Escape identifier (i.e. table name, field name) */ +PHP_FUNCTION(pg_escape_identifier) +{ + php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + + /* {{{ proto string pg_result_error(resource result) Get error message associated with result */ PHP_FUNCTION(pg_result_error) @@ -6214,7 +6349,7 @@ const size_t element_len = strlen(element); if (PG(magic_quotes_runtime)) { - data = php_addslashes(element, element_len, &data_len, 0 TSRMLS_CC); + data = php_addslashes(element, element_len, (int *)&data_len, 0 TSRMLS_CC); } else { data = safe_estrndup(element, element_len); data_len = element_len;
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php