Skip to content

Instantly share code, notes, and snippets.

@yohgaki
Created February 15, 2014 09:36
Show Gist options
  • Select an option

  • Save yohgaki/9016767 to your computer and use it in GitHub Desktop.

Select an option

Save yohgaki/9016767 to your computer and use it in GitHub Desktop.
ext/pgsql refactor and cleanup
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 2ac0454..c4632ae 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -746,6 +746,104 @@ 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. XXX: Black list could be wrong. False positive. */
+ 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, "WIN874", sizeof("WIN874")-1) ||
+ !strncmp(encoding, "WIN1258", sizeof("WIN1258")-1) ||
+ !strncmp(encoding, "BIG5", sizeof("BIG5")-1) ||
+ !strncmp(encoding, "GBK", sizeof("GBK")-1) ||
+ !strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) ||
+ !strncmp(encoding, "UHC", sizeof("UHC")-1) ) {
+ 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)
{
@@ -944,36 +1042,6 @@ 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)
@@ -1457,6 +1525,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)
@@ -2266,7 +2335,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) {
@@ -2296,7 +2364,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);
@@ -3053,7 +3120,7 @@ PHP_FUNCTION(pg_lo_create)
if (oid) {
#ifndef HAVE_PG_LO_CREATE
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "OID value passing not supported");
+ 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:
@@ -3734,10 +3801,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));
@@ -4114,7 +4177,6 @@ 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);
@@ -4302,61 +4364,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:
@@ -4384,30 +4398,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);
+ tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
} else {
- tmp = PQescapeIdentifier(pgsql, from, (size_t)from_len);
+ tmp = PGSQLescapeIdentifier(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
+ PGSQLfree(tmp);
RETURN_STRING(to, 0);
}
@@ -4455,6 +4457,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 */
@@ -4500,6 +4503,7 @@ PHP_FUNCTION(pg_result_error_field)
/* }}} */
#endif
+
/* {{{ proto int pg_connection_status(resource connection)
Get connection status */
PHP_FUNCTION(pg_connection_status)
@@ -4520,6 +4524,7 @@ PHP_FUNCTION(pg_connection_status)
/* }}} */
+
#if HAVE_PGTRANSACTIONSTATUS
/* {{{ proto int pg_transaction_status(resource connection)
Get transaction status */
@@ -4542,6 +4547,7 @@ PHP_FUNCTION(pg_transaction_status)
/* }}} */
+
/* {{{ proto bool pg_connection_reset(resource connection)
Reset connection (reconnect) */
PHP_FUNCTION(pg_connection_reset)
@@ -4563,12 +4569,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)
@@ -4589,6 +4596,7 @@ static int php_pgsql_flush_query(PGconn *pgsql TSRMLS_DC)
}
/* }}} */
+
/* {{{ php_pgsql_do_async
*/
static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
@@ -5126,25 +5134,17 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z
"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 = '");
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 = '");
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);
@@ -5234,6 +5234,7 @@ PHP_FUNCTION(pg_meta_data)
}
/* }}} */
+
/* {{{ php_pgsql_get_data_type
*/
static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
@@ -5641,17 +5642,11 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con
}
}
else {
- Z_TYPE_P(new_val) = IS_STRING;
-#if HAVE_PQESCAPE_CONN
- {
char *tmp;
+ Z_TYPE_P(new_val) = IS_STRING;
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
php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
}
break;
@@ -6043,16 +6038,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);
- free(escaped);
+ PGSQLfree(escaped);
+ }
}
} /* for */
zval_dtor(meta);
@@ -6139,29 +6130,21 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const c
if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) {
escaped = _php_pgsql_strndup(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);
- free(escaped);
+ PGSQLfree(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);
} 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);
- free(escaped);
+ PGSQLfree(escaped);
}
efree(table_copy);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment