Skip to content

Instantly share code, notes, and snippets.

@yohgaki
Created November 20, 2011 23:49
Show Gist options
  • Save yohgaki/1381181 to your computer and use it in GitHub Desktop.
Save yohgaki/1381181 to your computer and use it in GitHub Desktop.
php-trunk pg_escape_literal patch
Index: ext/pgsql/tests/08escape.phpt
===================================================================
--- ext/pgsql/tests/08escape.phpt (リビジョン 319557)
+++ 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,37 @@
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 (リビジョン 319557)
+++ ext/pgsql/php_pgsql.h (作業コピー)
@@ -172,6 +172,8 @@
PHP_FUNCTION(pg_escape_string);
PHP_FUNCTION(pg_escape_bytea);
PHP_FUNCTION(pg_unescape_bytea);
+PHP_FUNCTION(pg_escape_literal);
+PHP_FUNCTION(pg_escape_identifier);
#endif
/* misc functions */
Index: ext/pgsql/config.m4
===================================================================
--- ext/pgsql/config.m4 (リビジョン 319557)
+++ 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 (リビジョン 318651)
+++ 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()
@@ -652,6 +663,8 @@
PHP_FE(pg_escape_string, arginfo_pg_escape_string)
PHP_FE(pg_escape_bytea, arginfo_pg_escape_bytea)
PHP_FE(pg_unescape_bytea, arginfo_pg_unescape_bytea)
+ PHP_FE(pg_escape_literal, arginfo_pg_escape_literal)
+ PHP_FE(pg_escape_identifier, arginfo_pg_escape_identifier)
#endif
#if HAVE_PQSETERRORVERBOSITY
PHP_FE(pg_set_error_verbosity, arginfo_pg_set_error_verbosity)
@@ -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, &notice->len);
+ notice->message = _php_pgsql_trim_message(message, (int *)&notice->len);
if (PGG(log_notices)) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s", notice->message);
}
@@ -4196,6 +4209,130 @@
/* }}} */
#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, *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);
+}
+/* }}} */
+#endif
+
+
/* {{{ proto string pg_result_error(resource result)
Get error message associated with result */
PHP_FUNCTION(pg_result_error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment