add pg_escape_identifier/pg_escape_literal

This commit is contained in:
Yasuo Ohgaki 2011-11-25 02:21:01 +00:00 committed by Stanislav Malyshev
parent 106e0a2e68
commit 655245afef
4 changed files with 173 additions and 6 deletions

View File

@ -94,6 +94,7 @@ if test "$PHP_PGSQL" != "no"; then
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

View File

@ -422,6 +422,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
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 @@ const zend_function_entry pgsql_functions[] = {
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 @@ static void _php_pgsql_notice_handler(void *resource_id, const char *message)
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);
}
@ -4206,6 +4219,130 @@ 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, *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)

View File

@ -172,6 +172,8 @@ PHP_FUNCTION(pg_set_error_verbosity);
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 */

View File

@ -11,8 +11,9 @@ define('FILE_NAME', dirname(__FILE__) . '/php.gif');
// 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 @@ else {
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