Refactor and cleanup. WS is cleaned up. Use -b if it is needed.

Added compatibility macros, PQescapeStringConn, PGSQLescapeLiteral/Identifier, PGSQLfree.
This commit is contained in:
Yasuo Ohgaki 2014-02-15 16:31:43 +09:00
parent 5cc851e83e
commit 832c21cabf
2 changed files with 268 additions and 285 deletions

View file

@ -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)
{
@ -774,10 +872,10 @@ static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
#define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
#define PHP_PQ_ERROR(text, pgsql) { \
char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
php_error_docref(NULL TSRMLS_CC, E_WARNING, text, msgbuf); \
efree(msgbuf); \
#define PHP_PQ_ERROR(text, pgsql) { \
char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
php_error_docref(NULL TSRMLS_CC, E_WARNING, text, msgbuf); \
efree(msgbuf); \
} \
/* {{{ php_pgsql_set_default_link
@ -851,10 +949,10 @@ static void _php_pgsql_notice_ptr_dtor(void **ptr)
{
php_pgsql_notice *notice = (php_pgsql_notice *)*ptr;
if (notice) {
efree(notice->message);
efree(notice);
notice = NULL;
}
efree(notice->message);
efree(notice);
notice = NULL;
}
}
/* }}} */
@ -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);
@ -2775,7 +2842,7 @@ PHP_FUNCTION(pg_fetch_all_columns)
array_init(return_value);
if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
return;
}
@ -3027,10 +3094,10 @@ PHP_FUNCTION(pg_untrace)
Create a large object */
PHP_FUNCTION(pg_lo_create)
{
zval *pgsql_link = NULL, *oid = NULL;
PGconn *pgsql;
Oid pgsql_oid, wanted_oid = InvalidOid;
int id = -1, argc = ZEND_NUM_ARGS();
zval *pgsql_link = NULL, *oid = NULL;
PGconn *pgsql;
Oid pgsql_oid, wanted_oid = InvalidOid;
int id = -1, argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(argc TSRMLS_CC, "|zz", &pgsql_link, &oid) == FAILURE) {
return;
@ -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:
@ -3318,8 +3385,8 @@ PHP_FUNCTION(pg_lo_close)
Read a large object */
PHP_FUNCTION(pg_lo_read)
{
zval *pgsql_id;
long len;
zval *pgsql_id;
long len;
int buf_len = PGSQL_LO_READ_BUF_SIZE, nbytes, argc = ZEND_NUM_ARGS();
char *buf;
pgLofp *pgsql;
@ -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);
@ -4194,7 +4256,7 @@ PHP_FUNCTION(pg_escape_bytea)
*/
static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen)
{
size_t buflen;
size_t buflen;
unsigned char *buffer,
*sp,
*bp;
@ -4254,7 +4316,7 @@ static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *
memcpy(buf, sp-2, 3);
buf[3] = '\0';
start = buf;
*bp = (unsigned char)strtoul(start, (char **)&end, 8);
*bp = (unsigned char)strtoul(start, (char **)&end, 8);
buflen -= 3;
state = 0;
}
@ -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);
} 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);
if (escape_literal) {
tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
} else {
tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
}
#else
to = php_pgsql_PQescapeInternal(pgsql, from, (size_t)from_len, escape_literal);
if (!to) {
if (!tmp) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape");
RETURN_FALSE;
}
#endif
to = estrdup(tmp);
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,11 +5234,12 @@ 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)
{
/* This is stupid way to do. I'll fix it when I decied how to support
/* This is stupid way to do. I'll fix it when I decied how to support
user defined types. (Yasuo) */
/* boolean */
@ -5641,17 +5642,11 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con
}
}
else {
char *tmp;
Z_TYPE_P(new_val) = IS_STRING;
#if HAVE_PQESCAPE_CONN
{
char *tmp;
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
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;
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);
PGSQLfree(escaped);
}
add_assoc_zval(result, escaped, new_val);
free(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);
}