From 2b7945ad3659a0741911fdde5104838400c4e88e Mon Sep 17 00:00:00 2001 From: Matteo Beccati Date: Thu, 25 Mar 2021 15:07:37 +0100 Subject: [PATCH] pdo pgsql parser --- .gitignore | 1 + ext/pdo/pdo_sql_parser.h | 32 ++++++++++++++++++++ ext/pdo/pdo_sql_parser.re | 11 ++++--- ext/pdo/php_pdo_driver.h | 11 ++++++- ext/pdo_pgsql/Makefile.frag | 7 +++++ ext/pdo_pgsql/Makefile.frag.w32 | 3 ++ ext/pdo_pgsql/config.m4 | 3 +- ext/pdo_pgsql/config.w32 | 3 +- ext/pdo_pgsql/pgsql_driver.c | 3 +- ext/pdo_pgsql/pgsql_sql_parser.re | 49 +++++++++++++++++++++++++++++++ ext/pdo_pgsql/php_pdo_pgsql_int.h | 2 ++ scripts/dev/genfiles | 5 ++++ 12 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 ext/pdo/pdo_sql_parser.h create mode 100644 ext/pdo_pgsql/Makefile.frag create mode 100644 ext/pdo_pgsql/Makefile.frag.w32 create mode 100644 ext/pdo_pgsql/pgsql_sql_parser.re diff --git a/.gitignore b/.gitignore index 78dcc1f7ac2ff..8ea68999c71e9 100644 --- a/.gitignore +++ b/.gitignore @@ -139,6 +139,7 @@ php /ext/json/json_scanner.c /ext/json/php_json_scanner_defs.h /ext/pdo/pdo_sql_parser.c +/ext/pdo_pgsql/pgsql_sql_parser.c /ext/phar/phar_path_check.c /ext/standard/url_scanner_ex.c /ext/standard/var_unserializer.c diff --git a/ext/pdo/pdo_sql_parser.h b/ext/pdo/pdo_sql_parser.h new file mode 100644 index 0000000000000..f8c303473e7be --- /dev/null +++ b/ext/pdo/pdo_sql_parser.h @@ -0,0 +1,32 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: George Schlossnagle | + +----------------------------------------------------------------------+ +*/ + +#define PDO_PARSER_TEXT 1 +#define PDO_PARSER_BIND 2 +#define PDO_PARSER_BIND_POS 3 +#define PDO_PARSER_ESCAPED_QUESTION 4 +#define PDO_PARSER_EOI 5 + +#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1 + +#define RET(i) {s->cur = cursor; return i; } +#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } + +#define YYCTYPE unsigned char +#define YYCURSOR cursor +#define YYLIMIT s->end +#define YYMARKER s->ptr +#define YYFILL(n) { RET(PDO_PARSER_EOI); } diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 6716401187ab9..d996767ad4c42 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -35,11 +35,7 @@ #define YYMARKER s->ptr #define YYFILL(n) { RET(PDO_PARSER_EOI); } -typedef struct Scanner { - const char *ptr, *cur, *tok, *end; -} Scanner; - -static int scan(Scanner *s) +static int default_scanner(pdo_parser_t *s) { const char *cursor = s->cur; @@ -81,7 +77,7 @@ static void free_param_name(zval *el) { PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string **outquery) { - Scanner s; + pdo_parser_t s; char *newbuffer; ptrdiff_t t; uint32_t bindno = 0; @@ -91,6 +87,9 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string struct pdo_bound_param_data *param; int query_type = PDO_PLACEHOLDER_NONE; struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL; + int (*scan)(pdo_parser_t *s); + + scan = stmt->dbh->methods->parser ? stmt->dbh->methods->parser : default_scanner; s.cur = ZSTR_VAL(inquery); s.end = s.cur + ZSTR_LEN(inquery) + 1; diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c1a01b3400754..f45e97874ce77 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -24,6 +24,7 @@ typedef struct _pdo_dbh_t pdo_dbh_t; typedef struct _pdo_dbh_object_t pdo_dbh_object_t; typedef struct _pdo_stmt_t pdo_stmt_t; typedef struct _pdo_row_t pdo_row_t; +typedef struct _pdo_parser_t pdo_parser_t; struct pdo_bound_param_data; PDO_API zend_string *php_pdo_int64_to_str(int64_t i64); @@ -35,7 +36,7 @@ PDO_API zend_string *php_pdo_int64_to_str(int64_t i64); # define FALSE 0 #endif -#define PDO_DRIVER_API 20170320 +#define PDO_DRIVER_API 20210325 enum pdo_param_type { PDO_PARAM_NULL, @@ -276,6 +277,9 @@ typedef void (*pdo_dbh_request_shutdown)(pdo_dbh_t *dbh); * with any zvals in the driver_data that would be freed if the handle is destroyed. */ typedef void (*pdo_dbh_get_gc_func)(pdo_dbh_t *dbh, zend_get_gc_buffer *buffer); +/* driver specific re2s sql parser, overrides the default one if present */ +typedef int (*pdo_dbh_sql_parser)(pdo_parser_t *s); + /* for adding methods to the dbh or stmt objects pointer to a list of driver specific functions. The convention is to prefix the function names using the PDO driver name; this will @@ -308,6 +312,7 @@ struct pdo_dbh_methods { /* if defined to NULL, PDO will use its internal transaction tracking state */ pdo_dbh_txn_func in_transaction; pdo_dbh_get_gc_func get_gc; + pdo_dbh_sql_parser parser; }; /* }}} */ @@ -648,6 +653,10 @@ struct _pdo_row_t { pdo_stmt_t *stmt; }; +struct _pdo_parser_t { + const char *ptr, *cur, *tok, *end; +}; + /* Call this in MINIT to register the PDO driver. * Registering the driver might fail and should be reported accordingly in MINIT. */ PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver); diff --git a/ext/pdo_pgsql/Makefile.frag b/ext/pdo_pgsql/Makefile.frag new file mode 100644 index 0000000000000..33abcfc79ef7c --- /dev/null +++ b/ext/pdo_pgsql/Makefile.frag @@ -0,0 +1,7 @@ +$(srcdir)/pgsql_sql_parser.c: $(srcdir)/pgsql_sql_parser.re + @(cd $(top_srcdir); \ + if test -f ./pgsql_sql_parser.re; then \ + $(RE2C) $(RE2C_FLAGS) --no-generation-date -o pgsql_sql_parser.c pgsql_sql_parser.re; \ + else \ + $(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_pgsql/pgsql_sql_parser.c ext/pdo_pgsql/pgsql_sql_parser.re; \ + fi) diff --git a/ext/pdo_pgsql/Makefile.frag.w32 b/ext/pdo_pgsql/Makefile.frag.w32 new file mode 100644 index 0000000000000..fb7cab96a441b --- /dev/null +++ b/ext/pdo_pgsql/Makefile.frag.w32 @@ -0,0 +1,3 @@ +ext\pdo_pgsql\pgsql_sql_parser.c: ext\pdo_pgsql\pgsql_sql_parser.re + cd $(PHP_SRC_DIR) + $(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_pgsql/pgsql_sql_parser.c ext/pdo_pgsql/pgsql_sql_parser.re diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 index 80ffd97ac2ece..282da789431ec 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -78,6 +78,7 @@ if test "$PHP_PDO_PGSQL" != "no"; then PHP_CHECK_PDO_INCLUDES - PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path) + PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c, $ext_shared,,-I$pdo_cv_inc_path) PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo) + PHP_ADD_MAKEFILE_FRAGMENT fi diff --git a/ext/pdo_pgsql/config.w32 b/ext/pdo_pgsql/config.w32 index cda62a64dba56..d6be4487c5809 100644 --- a/ext/pdo_pgsql/config.w32 +++ b/ext/pdo_pgsql/config.w32 @@ -5,7 +5,7 @@ ARG_WITH("pdo-pgsql", "PostgreSQL support for PDO", "no"); if (PHP_PDO_PGSQL != "no") { if (CHECK_LIB("libpq.lib", "pdo_pgsql", PHP_PDO_PGSQL) && CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) { - EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c"); + EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c"); if (X64) { ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PG_LO64=1"); @@ -14,6 +14,7 @@ if (PHP_PDO_PGSQL != "no") { AC_DEFINE('HAVE_PDO_PGSQL', 1, 'Have PostgreSQL library'); ADD_EXTENSION_DEP('pdo_pgsql', 'pdo'); + ADD_MAKEFILE_FRAGMENT(); } else { WARNING("pdo_pgsql not enabled; libraries and headers not found"); } diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 1c511c1d47972..ec22f492d4506 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -1191,7 +1191,8 @@ static const struct pdo_dbh_methods pgsql_methods = { pdo_pgsql_get_driver_methods, /* get_driver_methods */ NULL, pgsql_handle_in_transaction, - NULL /* get_gc */ + NULL, /* get_gc */ + pdo_pgsql_parser }; static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ diff --git a/ext/pdo_pgsql/pgsql_sql_parser.re b/ext/pdo_pgsql/pgsql_sql_parser.re new file mode 100644 index 0000000000000..eb7c9811ca92b --- /dev/null +++ b/ext/pdo_pgsql/pgsql_sql_parser.re @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Matteo Beccati | + +----------------------------------------------------------------------+ +*/ + + +#include "php.h" +#include "pdo/php_pdo_driver.h" +#include "pdo/php_pdo_int.h" +#include "pdo/pdo_sql_parser.h" + +int pdo_pgsql_parser(pdo_parser_t *s) +{ + const char *cursor = s->cur; + + s->tok = cursor; + /*!re2c + BINDCHR = [:][a-zA-Z0-9_]+; + QUESTION = [?]; + ESCQUESTION = [?][?]; + COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*); + SPECIALS = [:?"'-/]; + MULTICHAR = [:]{2,}; + ANYNOEOF = [\001-\377]; + */ + + /*!re2c + (["]((["]["])|ANYNOEOF)*["]) { RET(PDO_PARSER_TEXT); } + (['](([']['])|ANYNOEOF)*[']) { RET(PDO_PARSER_TEXT); } + MULTICHAR { RET(PDO_PARSER_TEXT); } + ESCQUESTION { RET(PDO_PARSER_ESCAPED_QUESTION); } + BINDCHR { RET(PDO_PARSER_BIND); } + QUESTION { RET(PDO_PARSER_BIND_POS); } + SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); } + COMMENTS { RET(PDO_PARSER_TEXT); } + (ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); } + */ +} diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 16aeb3ca48ad4..45b88cfbd0e89 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -107,4 +107,6 @@ extern const php_stream_ops pdo_pgsql_lob_stream_ops; void pdo_libpq_version(char *buf, size_t len); +int pdo_pgsql_parser(pdo_parser_t *s); + #endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/scripts/dev/genfiles b/scripts/dev/genfiles index 3e085c3e5397f..aa8b095a030d9 100755 --- a/scripts/dev/genfiles +++ b/scripts/dev/genfiles @@ -124,6 +124,11 @@ $MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo builddir=ext/pdo top_ -f ext/pdo/Makefile.frag \ ext/pdo/pdo_sql_parser.c +echo "genfiles: Generating PDO_pgsql lexer file" +$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_pgsql builddir=ext/pdo_pgsql top_srcdir=. \ + -f ext/pdo_pgsql/Makefile.frag \ + ext/pdo_pgsql/pgsql_sql_parser.c + echo "genfiles: Generating standard extension lexer files" $MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/standard builddir=ext/standard top_srcdir=. \ -f ext/standard/Makefile.frag \