mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge remote-tracking branch 'mbeccati/pdo_driver_specific_parser'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -137,7 +137,7 @@ php
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_scanner.c
|
||||
/ext/json/php_json_scanner_defs.h
|
||||
/ext/pdo/pdo_sql_parser.c
|
||||
/ext/pdo*/*_sql_parser.c
|
||||
/ext/phar/phar_path_check.c
|
||||
/ext/standard/url_scanner_ex.c
|
||||
/ext/standard/var_unserializer.c
|
||||
|
||||
4
NEWS
4
NEWS
@@ -171,6 +171,7 @@ PHP NEWS
|
||||
- PDO:
|
||||
. Fixed setAttribute and getAttribute. (SakiTakamachi)
|
||||
. Implemented PDO driver-specific subclasses RFC. (danack, kocsismate)
|
||||
. Added support for PDO driver-specific SQL parsers. (Matteo Beccati)
|
||||
|
||||
- PDO_DBLIB:
|
||||
. Fixed setAttribute and getAttribute. (SakiTakamachi)
|
||||
@@ -185,6 +186,7 @@ PHP NEWS
|
||||
- PDO_MYSQL:
|
||||
. Fixed setAttribute and getAttribute. (SakiTakamachi)
|
||||
. Added class Pdo\Mysql. (danack, kocsismate)
|
||||
. Added custom SQL parser. (Matteo Beccati)
|
||||
|
||||
- PDO_ODBC:
|
||||
. Added class Pdo\Odbc. (danack, kocsismate)
|
||||
@@ -197,11 +199,13 @@ PHP NEWS
|
||||
. Retrieve the memory usage of the query result resource. (KentarouTakeda)
|
||||
. Added Pdo\Pgsql::setNoticeCallBack method to receive DB notices.
|
||||
(outtersg)
|
||||
. Added custom SQL parser. (Matteo Beccati)
|
||||
|
||||
- PDO_SQLITE:
|
||||
. Added class Pdo\Sqlite. (danack, kocsismate)
|
||||
. Fixed bug #81227 (PDO::inTransaction reports false when in transaction).
|
||||
(nielsdos)
|
||||
. Added custom SQL parser. (Matteo Beccati)
|
||||
|
||||
- PGSQL:
|
||||
. Added the possibility to have no conditions for pg_select. (OmarEmaraDev)
|
||||
|
||||
31
UPGRADING
31
UPGRADING
@@ -240,6 +240,32 @@ PHP 8.4 UPGRADE NOTES
|
||||
openssl_pkey_get_details as well as openssl_sign and openssl_verify were
|
||||
extended to support those keys.
|
||||
|
||||
- PDO:
|
||||
. Added support for driver specific SQL parsers. The default parser supports:
|
||||
- single and double quoted literals, with doubling as escaping mechanism.
|
||||
- two-dashes and non-nested C-style comments.
|
||||
|
||||
- PDO_MYSQL:
|
||||
. Added custom parser supporting:
|
||||
- single and double-quoted literals, with doubling and backslash as escaping
|
||||
mechanism
|
||||
- backtick literal identifiers and with doubling as escaping mechanism
|
||||
- two dashes followed by at least 1 whitespace, non-nested C-style comments,
|
||||
and hash-comments
|
||||
|
||||
- PDO_PGSQL:
|
||||
. Added custom parser supporting:
|
||||
- single and double quoted literals, with doubling as escaping mechanism
|
||||
- C-style "escape" string literals (E'string')
|
||||
- dollar-quoted string literals
|
||||
- two-dashes and C-style comments (non-nested)
|
||||
- support for "??" as escape sequence for the "?" operator
|
||||
|
||||
- PDO_SQLITE:
|
||||
. Added custom parser supporting:
|
||||
- single, double quoted, and backtick literals, with doubling as escaping mechanism
|
||||
- square brackets quoting for identifiers
|
||||
- two-dashes and C-style comments (non-nested)
|
||||
|
||||
- Phar:
|
||||
. Added support for the unix timestamp extension for zip archives.
|
||||
@@ -355,6 +381,11 @@ PHP 8.4 UPGRADE NOTES
|
||||
. Calling ldap_exop() with more than 4 arguments is deprecated. Use
|
||||
ldap_exop_sync() instead.
|
||||
|
||||
- PDO_PGSQL:
|
||||
. Using escaped question marks (??) inside dollar-quoted strings is deprecated.
|
||||
Since PDO_PGSQL has its own SQL parser with dollar-quoted strings support, it
|
||||
is no longer necessary to escape question marks inside them.
|
||||
|
||||
- PgSQL:
|
||||
. Calling pgsql_fetch_result() with 2 arguments is deprecated. Use the
|
||||
3-parameter signature with a null $row parameter instead.
|
||||
|
||||
33
ext/pdo/pdo_sql_parser.h
Normal file
33
ext/pdo/pdo_sql_parser.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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 <george@omniti.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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_CUSTOM_QUOTE 5
|
||||
#define PDO_PARSER_EOI 6
|
||||
|
||||
#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) { if (YYLIMIT - 1 <= YYCURSOR) RET(PDO_PARSER_EOI); }
|
||||
@@ -17,29 +17,9 @@
|
||||
#include "php.h"
|
||||
#include "php_pdo_driver.h"
|
||||
#include "php_pdo_int.h"
|
||||
#include "pdo_sql_parser.h"
|
||||
|
||||
#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); }
|
||||
|
||||
typedef struct Scanner {
|
||||
const char *ptr, *cur, *tok, *end;
|
||||
} Scanner;
|
||||
|
||||
static int scan(Scanner *s)
|
||||
static int default_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
@@ -47,18 +27,16 @@ static int scan(Scanner *s)
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
ESCQUESTION = [?][?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
|
||||
SPECIALS = [:?"'-/];
|
||||
MULTICHAR = [:]{2,};
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--".*);
|
||||
SPECIALS = [:?"'/-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
(["]((["]["])|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); }
|
||||
@@ -75,13 +53,18 @@ struct placeholder {
|
||||
struct placeholder *next;
|
||||
};
|
||||
|
||||
struct custom_quote {
|
||||
const char *pos;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void free_param_name(zval *el) {
|
||||
zend_string_release(Z_PTR_P(el));
|
||||
}
|
||||
|
||||
PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string **outquery)
|
||||
{
|
||||
Scanner s;
|
||||
pdo_scanner_t s;
|
||||
char *newbuffer;
|
||||
ptrdiff_t t;
|
||||
uint32_t bindno = 0;
|
||||
@@ -91,12 +74,42 @@ 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_scanner_t *s);
|
||||
struct custom_quote custom_quote = {NULL, 0};
|
||||
|
||||
scan = stmt->dbh->methods->scanner ? stmt->dbh->methods->scanner : default_scanner;
|
||||
|
||||
s.cur = ZSTR_VAL(inquery);
|
||||
s.end = s.cur + ZSTR_LEN(inquery) + 1;
|
||||
|
||||
/* phase 1: look for args */
|
||||
while((t = scan(&s)) != PDO_PARSER_EOI) {
|
||||
if (custom_quote.pos) {
|
||||
/* Inside a custom quote */
|
||||
if (t == PDO_PARSER_CUSTOM_QUOTE && custom_quote.len == s.cur - s.tok && !strncmp(s.tok, custom_quote.pos, custom_quote.len)) {
|
||||
/* Matching closing quote found, end custom quoting */
|
||||
custom_quote.pos = NULL;
|
||||
custom_quote.len = 0;
|
||||
} else if (t == PDO_PARSER_ESCAPED_QUESTION) {
|
||||
/* An escaped question mark has been used inside a dollar quoted string, most likely as a workaround
|
||||
* as a single "?" would have been parsed as placeholder, due to the lack of support for dollar quoted
|
||||
* strings. For now, we emit a deprecation notice, but still process it */
|
||||
php_error_docref(NULL, E_DEPRECATED, "Escaping question marks inside dollar quoted strings is not required anymore and is deprecated");
|
||||
|
||||
goto placeholder;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t == PDO_PARSER_CUSTOM_QUOTE) {
|
||||
/* Start of a custom quote, keep a reference to search for the matching closing quote */
|
||||
custom_quote.pos = s.tok;
|
||||
custom_quote.len = s.cur - s.tok;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS || t == PDO_PARSER_ESCAPED_QUESTION) {
|
||||
if (t == PDO_PARSER_ESCAPED_QUESTION && stmt->supports_placeholders == PDO_PLACEHOLDER_POSITIONAL) {
|
||||
/* escaped question marks unsupported, treat as text */
|
||||
@@ -113,6 +126,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
|
||||
query_type |= PDO_PLACEHOLDER_POSITIONAL;
|
||||
}
|
||||
|
||||
placeholder:
|
||||
plc = emalloc(sizeof(*plc));
|
||||
memset(plc, 0, sizeof(*plc));
|
||||
plc->next = NULL;
|
||||
|
||||
@@ -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_scanner_t pdo_scanner_t;
|
||||
struct pdo_bound_param_data;
|
||||
|
||||
#ifndef TRUE
|
||||
@@ -33,7 +34,7 @@ struct pdo_bound_param_data;
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#define PDO_DRIVER_API 20170320
|
||||
#define PDO_DRIVER_API 20240423
|
||||
|
||||
/* Doctrine hardcodes these constants, avoid changing their values. */
|
||||
enum pdo_param_type {
|
||||
@@ -275,6 +276,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_scanner)(pdo_scanner_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
|
||||
@@ -307,6 +311,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_scanner scanner;
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@@ -647,6 +652,10 @@ struct _pdo_row_t {
|
||||
pdo_stmt_t *stmt;
|
||||
};
|
||||
|
||||
struct _pdo_scanner_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);
|
||||
|
||||
@@ -436,7 +436,8 @@ static const struct pdo_dbh_methods dblib_methods = {
|
||||
NULL, /* get driver methods */
|
||||
NULL, /* request shutdown */
|
||||
NULL, /* in transaction, use PDO's internal tracking mechanism */
|
||||
NULL /* get gc */
|
||||
NULL, /* get gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
|
||||
static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
|
||||
|
||||
@@ -1274,7 +1274,8 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
|
||||
NULL, /* get driver methods */
|
||||
NULL, /* request shutdown */
|
||||
pdo_firebird_in_manually_transaction,
|
||||
NULL /* get gc */
|
||||
NULL, /* get gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
/* }}} */
|
||||
|
||||
|
||||
7
ext/pdo_mysql/Makefile.frag
Normal file
7
ext/pdo_mysql/Makefile.frag
Normal file
@@ -0,0 +1,7 @@
|
||||
$(srcdir)/mysql_sql_parser.c: $(srcdir)/mysql_sql_parser.re
|
||||
@(cd $(top_srcdir); \
|
||||
if test -f ./mysql_sql_parser.re; then \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o mysql_sql_parser.c mysql_sql_parser.re; \
|
||||
else \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_mysql/mysql_sql_parser.c ext/pdo_mysql/mysql_sql_parser.re; \
|
||||
fi)
|
||||
3
ext/pdo_mysql/Makefile.frag.w32
Normal file
3
ext/pdo_mysql/Makefile.frag.w32
Normal file
@@ -0,0 +1,3 @@
|
||||
ext\pdo_mysql\mysql_sql_parser.c: ext\pdo_mysql\mysql_sql_parser.re
|
||||
cd $(PHP_SRC_DIR)
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_mysql/mysql_sql_parser.c ext/pdo_mysql/mysql_sql_parser.re
|
||||
@@ -85,9 +85,10 @@ if test "$PHP_PDO_MYSQL" != "no"; then
|
||||
AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ])
|
||||
fi
|
||||
|
||||
PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
|
||||
PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
|
||||
if test "$PHP_PDO_MYSQL" = "yes" || test "$PHP_PDO_MYSQL" = "mysqlnd"; then
|
||||
PHP_ADD_EXTENSION_DEP(pdo_mysql, mysqlnd)
|
||||
|
||||
@@ -6,15 +6,17 @@ if (PHP_PDO_MYSQL != "no") {
|
||||
if (PHP_PDO_MYSQL == "yes" || PHP_PDO_MYSQL == "mysqlnd") {
|
||||
AC_DEFINE('PDO_USE_MYSQLND', 1, 'Using MySQL native driver');
|
||||
STDOUT.WriteLine("INFO: mysqlnd build");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c");
|
||||
ADD_EXTENSION_DEP('pdo_mysql', 'pdo');
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) &&
|
||||
CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL",
|
||||
PHP_PDO_MYSQL + "\\include;" +
|
||||
PHP_PHP_BUILD + "\\include\\mysql;" +
|
||||
PHP_PDO_MYSQL)) {
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
WARNING("pdo_mysql not enabled; libraries and headers not found");
|
||||
}
|
||||
|
||||
@@ -650,7 +650,8 @@ static const struct pdo_dbh_methods mysql_methods = {
|
||||
NULL,
|
||||
pdo_mysql_request_shutdown,
|
||||
pdo_mysql_in_transaction,
|
||||
NULL /* get_gc */
|
||||
NULL, /* get_gc */
|
||||
pdo_mysql_scanner
|
||||
};
|
||||
/* }}} */
|
||||
|
||||
|
||||
48
ext/pdo_mysql/mysql_sql_parser.re
Normal file
48
ext/pdo_mysql/mysql_sql_parser.re
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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: |
|
||||
| https://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 <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_mysql_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|(("--"[ \t\v\f\r])|[#]).*);
|
||||
SPECIALS = [:?"'`/#-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["]((["]["])|([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
([`]([`][`]|ANYNOEOF\[`])*[`]) { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
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); }
|
||||
*/
|
||||
}
|
||||
@@ -147,6 +147,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_mysql_driver;
|
||||
|
||||
extern int pdo_mysql_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);
|
||||
#define pdo_mysql_error(s) _pdo_mysql_error(s, NULL, __FILE__, __LINE__)
|
||||
#define pdo_mysql_error_stmt(s) _pdo_mysql_error(stmt->dbh, stmt, __FILE__, __LINE__)
|
||||
|
||||
68
ext/pdo_mysql/tests/pdo_mysql_parser.phpt
Normal file
68
ext/pdo_mysql/tests/pdo_mysql_parser.phpt
Normal file
@@ -0,0 +1,68 @@
|
||||
--TEST--
|
||||
MySQL PDO Parser custom syntax
|
||||
--EXTENSIONS--
|
||||
pdo_mysql
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
|
||||
MySQLPDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
|
||||
$db = MySQLPDOTest::factory();
|
||||
|
||||
$table = 'pdo_mysql_parser';
|
||||
|
||||
$db->exec("DROP TABLE IF EXISTS {$table}");
|
||||
$db->exec("CREATE TABLE {$table} (`a``?` int NOT NULL)");
|
||||
$db->exec("INSERT INTO {$table} VALUES (1)");
|
||||
|
||||
// No parameters
|
||||
$queries = [
|
||||
"SELECT * FROM {$table}",
|
||||
"SELECT * FROM {$table} -- ?",
|
||||
"SELECT * FROM {$table} # ?",
|
||||
"SELECT * FROM {$table} /* ? */",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
var_dump($query, $stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
// One parameter
|
||||
$queries = [
|
||||
"SELECT * FROM {$table} WHERE 1 = ?",
|
||||
"SELECT * FROM {$table} WHERE 1 = --?",
|
||||
"SELECT * FROM {$table} WHERE \"?\" IN (?, '?')",
|
||||
"SELECT * FROM {$table} WHERE `a``?` = ?",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute([1]);
|
||||
var_dump($query, $stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
$db->exec("DROP TABLE pdo_mysql_parser");
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(30) "SELECT * FROM pdo_mysql_parser"
|
||||
bool(true)
|
||||
string(35) "SELECT * FROM pdo_mysql_parser -- ?"
|
||||
bool(true)
|
||||
string(34) "SELECT * FROM pdo_mysql_parser # ?"
|
||||
bool(true)
|
||||
string(38) "SELECT * FROM pdo_mysql_parser /* ? */"
|
||||
bool(true)
|
||||
string(42) "SELECT * FROM pdo_mysql_parser WHERE 1 = ?"
|
||||
bool(true)
|
||||
string(44) "SELECT * FROM pdo_mysql_parser WHERE 1 = --?"
|
||||
bool(true)
|
||||
string(52) "SELECT * FROM pdo_mysql_parser WHERE "?" IN (?, '?')"
|
||||
bool(true)
|
||||
string(47) "SELECT * FROM pdo_mysql_parser WHERE `a``?` = ?"
|
||||
bool(true)
|
||||
@@ -461,7 +461,8 @@ static const struct pdo_dbh_methods odbc_methods = {
|
||||
NULL, /* get_driver_methods */
|
||||
NULL, /* request_shutdown */
|
||||
NULL, /* in transaction, use PDO's internal tracking mechanism */
|
||||
NULL /* get_gc */
|
||||
NULL, /* get_gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
|
||||
static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
|
||||
|
||||
7
ext/pdo_pgsql/Makefile.frag
Normal file
7
ext/pdo_pgsql/Makefile.frag
Normal file
@@ -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)
|
||||
3
ext/pdo_pgsql/Makefile.frag.w32
Normal file
3
ext/pdo_pgsql/Makefile.frag.w32
Normal file
@@ -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
|
||||
@@ -79,6 +79,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)
|
||||
PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c, $ext_shared)
|
||||
PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
fi
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -1321,7 +1321,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_scanner
|
||||
};
|
||||
|
||||
static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
|
||||
|
||||
53
ext/pdo_pgsql/pgsql_sql_parser.re
Normal file
53
ext/pdo_pgsql/pgsql_sql_parser.re
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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 <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_pgsql_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
ESCQUESTION = [?][?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--".*);
|
||||
DOLQ_START = [A-Za-z\200-\377_];
|
||||
DOLQ_CONT = [A-Za-z\200-\377_0-9];
|
||||
SPECIALS = [$eE:?"'/-];
|
||||
MULTICHAR = [:]{2,};
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
[eE](['](([']['])|([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
(["]((["]["])|ANYNOEOF\["])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|ANYNOEOF\['])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
[$](DOLQ_START DOLQ_CONT*)?[$] { RET(PDO_PARSER_CUSTOM_QUOTE); }
|
||||
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); }
|
||||
*/
|
||||
}
|
||||
@@ -74,6 +74,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_pgsql_driver;
|
||||
|
||||
extern int pdo_pgsql_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);
|
||||
#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)
|
||||
#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)
|
||||
|
||||
47
ext/pdo_pgsql/tests/bug_14244.phpt
Normal file
47
ext/pdo_pgsql/tests/bug_14244.phpt
Normal file
@@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
PDO PgSQL Bug #14244 (Postgres sees parameters in a dollar-delimited string)
|
||||
--EXTENSIONS--
|
||||
pdo
|
||||
pdo_pgsql
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require __DIR__ . '/config.inc';
|
||||
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
echo "Test\n";
|
||||
|
||||
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
|
||||
$pdo = PDOTest::test_factory(__DIR__ . '/common.phpt');
|
||||
$pdo->setAttribute (\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute (\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
|
||||
echo "Already working (see bug64953.phpt):\n";
|
||||
|
||||
$st = $pdo->prepare("SELECT '?' question");
|
||||
$st->execute();
|
||||
var_dump($st->fetch());
|
||||
|
||||
echo "Inside a dollar-quoted string:\n";
|
||||
|
||||
$st = $pdo->prepare("SELECT \$\$?\$\$ question");
|
||||
$st->execute();
|
||||
var_dump($st->fetch());
|
||||
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Test
|
||||
Already working (see bug64953.phpt):
|
||||
array(1) {
|
||||
["question"]=>
|
||||
string(1) "?"
|
||||
}
|
||||
Inside a dollar-quoted string:
|
||||
array(1) {
|
||||
["question"]=>
|
||||
string(1) "?"
|
||||
}
|
||||
Done
|
||||
67
ext/pdo_pgsql/tests/pdo_pgsql_parser.phpt
Normal file
67
ext/pdo_pgsql/tests/pdo_pgsql_parser.phpt
Normal file
@@ -0,0 +1,67 @@
|
||||
--TEST--
|
||||
PgSQL PDO Parser custom syntax
|
||||
--EXTENSIONS--
|
||||
pdo
|
||||
pdo_pgsql
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require __DIR__ . '/config.inc';
|
||||
require dirname(__DIR__, 2) . '/pdo/tests/pdo_test.inc';
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
|
||||
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$query = <<<'EOF'
|
||||
SELECT e'He''ll\'o? ' || ? AS b -- '
|
||||
UNION ALL
|
||||
SELECT 'He''ll''o?\' || ? AS b -- '
|
||||
UNION ALL
|
||||
SELECT U&'d\0061t\+000061? ' || ? AS b /* :name */
|
||||
UNION ALL
|
||||
SELECT $__$Is this a $$dollar$$ 'escaping'? $__$ || ? AS b -- ?
|
||||
UNION ALL
|
||||
SELECT $$Escaped question mark here?? $$ || ? AS b -- ?
|
||||
EOF;
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute(['World', 'World', 'base', 'Yes', 'Yes']);
|
||||
|
||||
while ($row = $stmt->fetchColumn()) {
|
||||
var_dump($row);
|
||||
}
|
||||
|
||||
// Nested comments are incompatible: PDO parses the "?" inside the comment, Postgres doesn't
|
||||
$query = <<<'EOF'
|
||||
SELECT 'Hello' || ? /* is this a /* nested */ 'comment' ? */
|
||||
EOF;
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
|
||||
try {
|
||||
$stmt->execute(['X', 'Y', 'Z']);
|
||||
} catch (PDOException $e) {
|
||||
// PDO error: 3 parameters vs 2 expected
|
||||
var_dump('HY093' === $e->getCode());
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt->execute(['X', 'Y']);
|
||||
} catch (PDOException $e) {
|
||||
// PgSQL error: Indeterminate datatype (1 extra parameter)
|
||||
var_dump('42P18' === $e->getCode());
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Deprecated: PDO::prepare(): Escaping question marks inside dollar quoted strings is not required anymore and is deprecated in %s on line %d
|
||||
string(14) "He'll'o? World"
|
||||
string(14) "He'll'o?\World"
|
||||
string(10) "data? base"
|
||||
string(36) "Is this a $$dollar$$ 'escaping'? Yes"
|
||||
string(31) "Escaped question mark here? Yes"
|
||||
bool(true)
|
||||
bool(true)
|
||||
7
ext/pdo_sqlite/Makefile.frag
Normal file
7
ext/pdo_sqlite/Makefile.frag
Normal file
@@ -0,0 +1,7 @@
|
||||
$(srcdir)/sqlite_sql_parser.c: $(srcdir)/sqlite_sql_parser.re
|
||||
@(cd $(top_srcdir); \
|
||||
if test -f ./sqlite_sql_parser.re; then \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o sqlite_sql_parser.c sqlite_sql_parser.re; \
|
||||
else \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_sqlite/sqlite_sql_parser.c ext/pdo_sqlite/sqlite_sql_parser.re; \
|
||||
fi)
|
||||
3
ext/pdo_sqlite/Makefile.frag.w32
Normal file
3
ext/pdo_sqlite/Makefile.frag.w32
Normal file
@@ -0,0 +1,3 @@
|
||||
ext\pdo_sqlite\sqlite_sql_parser.c: ext\pdo_sqlite\sqlite_sql_parser.re
|
||||
cd $(PHP_SRC_DIR)
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_sqlite/sqlite_sql_parser.c ext/pdo_sqlite/sqlite_sql_parser.re
|
||||
@@ -29,8 +29,9 @@ if test "$PHP_PDO_SQLITE" != "no"; then
|
||||
)
|
||||
|
||||
PHP_SUBST(PDO_SQLITE_SHARED_LIBADD)
|
||||
PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c,
|
||||
PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c sqlite_sql_parser.c,
|
||||
$ext_shared)
|
||||
|
||||
PHP_ADD_EXTENSION_DEP(pdo_sqlite, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
fi
|
||||
|
||||
@@ -4,11 +4,12 @@ ARG_WITH("pdo-sqlite", "for pdo_sqlite support", "no");
|
||||
|
||||
if (PHP_PDO_SQLITE != "no") {
|
||||
if (SETUP_SQLITE3("pdo_sqlite", PHP_PDO_SQLITE, PHP_PDO_SQLITE_SHARED)) {
|
||||
EXTENSION("pdo_sqlite", "pdo_sqlite.c sqlite_driver.c sqlite_statement.c");
|
||||
EXTENSION("pdo_sqlite", "pdo_sqlite.c sqlite_driver.c sqlite_statement.c sqlite_sql_parser.c");
|
||||
|
||||
ADD_EXTENSION_DEP('pdo_sqlite', 'pdo');
|
||||
AC_DEFINE("HAVE_SQLITE3_COLUMN_TABLE_NAME", 1, "have sqlite3_column_table_name");
|
||||
AC_DEFINE("HAVE_SQLITE3_CLOSE_V2", 1, "have sqlite3_close_v2");
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
WARNING("pdo_sqlite not enabled; libraries and/or headers not found");
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_sqlite_driver;
|
||||
|
||||
extern int pdo_sqlite_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);
|
||||
#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__)
|
||||
#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__)
|
||||
|
||||
@@ -743,7 +743,8 @@ static const struct pdo_dbh_methods sqlite_methods = {
|
||||
get_driver_methods,
|
||||
pdo_sqlite_request_shutdown,
|
||||
pdo_sqlite_in_transaction,
|
||||
pdo_sqlite_get_gc
|
||||
pdo_sqlite_get_gc,
|
||||
pdo_sqlite_scanner
|
||||
};
|
||||
|
||||
static char *make_filename_safe(const char *filename)
|
||||
|
||||
49
ext/pdo_sqlite/sqlite_sql_parser.re
Normal file
49
ext/pdo_sqlite/sqlite_sql_parser.re
Normal file
@@ -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: |
|
||||
| https://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 <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_sqlite_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--".*);
|
||||
SPECIALS = [:?"'`/[-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["]((["]["])|ANYNOEOF)*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|ANYNOEOF)*[']) { RET(PDO_PARSER_TEXT); }
|
||||
([`](([`][`])|ANYNOEOF)*[`]) { RET(PDO_PARSER_TEXT); }
|
||||
("["ANYNOEOF*"]") { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
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); }
|
||||
*/
|
||||
}
|
||||
62
ext/pdo_sqlite/tests/pdo_sqlite_parser.phpt
Normal file
62
ext/pdo_sqlite/tests/pdo_sqlite_parser.phpt
Normal file
@@ -0,0 +1,62 @@
|
||||
--TEST--
|
||||
PDO_sqlite: Parser custom syntax
|
||||
--EXTENSIONS--
|
||||
pdo_sqlite
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_parser.db";
|
||||
|
||||
// Default open flag is read-write|create
|
||||
$db = new PDO('sqlite:' . $filename, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
||||
|
||||
$table = 'pdo_sqlite_parser';
|
||||
|
||||
$db->exec("CREATE TABLE {$table} (`a``?` int NOT NULL)");
|
||||
$db->exec("INSERT INTO {$table} VALUES (1)");
|
||||
|
||||
// No parameters
|
||||
$queries = [
|
||||
"SELECT * FROM {$table}",
|
||||
"SELECT * FROM {$table} -- ?",
|
||||
"SELECT * FROM {$table} /* ? */",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
// One parameter
|
||||
$queries = [
|
||||
"SELECT * FROM {$table} WHERE '1' = ?",
|
||||
"SELECT * FROM {$table} WHERE \"?\" IN (?, \"?\")",
|
||||
"SELECT * FROM {$table} WHERE `a``?` = ?",
|
||||
"SELECT * FROM {$table} WHERE \"a`?\" = ?",
|
||||
"SELECT * FROM {$table} WHERE [a`?] = ?",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute([1]);
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
$filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_parser.db";
|
||||
if (file_exists($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -125,6 +125,21 @@ $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_mysql lexer file"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_mysql builddir=ext/pdo_mysql top_srcdir=. \
|
||||
-f ext/pdo_mysql/Makefile.frag \
|
||||
ext/pdo_mysql/mysql_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 PDO_sqlite lexer file"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_sqlite builddir=ext/pdo_sqlite top_srcdir=. \
|
||||
-f ext/pdo_sqlite/Makefile.frag \
|
||||
ext/pdo_sqlite/sqlite_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 \
|
||||
|
||||
Reference in New Issue
Block a user