Rewrite DB param lookup
* Correct wrong comment lookup * simplify regex by excluding comment and string blocks before * simpler lookup for each type * update checks for more tests for various special cases In DB IO * add a function to return all placeholders found in a query * only numbered parameters are looked up
This commit is contained in:
@@ -4744,7 +4744,7 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
|
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
|
||||||
// all hast to be string
|
// all hast to be string
|
||||||
foreach ($res as $key => $value) {
|
foreach ($res as $key => $value) {
|
||||||
$this->assertIsString($value, 'Aseert string for column: ' . $key);
|
$this->assertIsString($value, 'Assert string for column: ' . $key);
|
||||||
}
|
}
|
||||||
// convert base only
|
// convert base only
|
||||||
$db->dbSetConvertFlag(Convert::on);
|
$db->dbSetConvertFlag(Convert::on);
|
||||||
@@ -4757,10 +4757,10 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
}
|
}
|
||||||
switch ($type_layout[$name]) {
|
switch ($type_layout[$name]) {
|
||||||
case 'int':
|
case 'int':
|
||||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4774,13 +4774,13 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
}
|
}
|
||||||
switch ($type_layout[$name]) {
|
switch ($type_layout[$name]) {
|
||||||
case 'int':
|
case 'int':
|
||||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'float':
|
case 'float':
|
||||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4794,17 +4794,17 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
}
|
}
|
||||||
switch ($type_layout[$name]) {
|
switch ($type_layout[$name]) {
|
||||||
case 'int':
|
case 'int':
|
||||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'float':
|
case 'float':
|
||||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
case 'jsonb':
|
case 'jsonb':
|
||||||
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
|
$this->assertIsArray($value, 'Assert array for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4818,25 +4818,25 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
}
|
}
|
||||||
switch ($type_layout[$name]) {
|
switch ($type_layout[$name]) {
|
||||||
case 'int':
|
case 'int':
|
||||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'float':
|
case 'float':
|
||||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
case 'jsonb':
|
case 'jsonb':
|
||||||
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
|
$this->assertIsArray($value, 'Assert array for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
case 'bytea':
|
case 'bytea':
|
||||||
// for hex types it must not start with \x
|
// for hex types it must not start with \x
|
||||||
$this->assertStringStartsNotWith(
|
$this->assertStringStartsNotWith(
|
||||||
'\x',
|
'\x',
|
||||||
$value,
|
$value,
|
||||||
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
|
'Assert bytes not starts with \x for column: ' . $key . '/' . $name
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5235,6 +5235,9 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
$3
|
$3
|
||||||
-- comment 3
|
-- comment 3
|
||||||
, $4
|
, $4
|
||||||
|
-- ignore $5, $6
|
||||||
|
-- $7, $8
|
||||||
|
-- digest($9, 10)
|
||||||
)
|
)
|
||||||
SQL,
|
SQL,
|
||||||
'count' => 4,
|
'count' => 4,
|
||||||
@@ -5305,6 +5308,20 @@ final class CoreLibsDBIOTest extends TestCase
|
|||||||
SQL,
|
SQL,
|
||||||
'count' => 2,
|
'count' => 2,
|
||||||
'convert' => false,
|
'convert' => false,
|
||||||
|
],
|
||||||
|
// a text string with escaped quite
|
||||||
|
'text string, with escaped quote' => [
|
||||||
|
'query' => <<<SQL
|
||||||
|
SELECT row_int
|
||||||
|
FROM table_with_primary_key
|
||||||
|
WHERE
|
||||||
|
row_varchar = 'foo bar bar baz $5' OR
|
||||||
|
row_varchar = 'foo bar '' barbar $6' OR
|
||||||
|
row_varchar = E'foo bar \' barbar $7' OR
|
||||||
|
row_varchar = CASE WHEN row_int = 1 THEN $1 ELSE $2 END
|
||||||
|
SQL,
|
||||||
|
'count' => 2,
|
||||||
|
'convert' => false,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ ob_end_flush();
|
|||||||
|
|
||||||
use CoreLibs\Debug\Support;
|
use CoreLibs\Debug\Support;
|
||||||
use CoreLibs\DB\Support\ConvertPlaceholder;
|
use CoreLibs\DB\Support\ConvertPlaceholder;
|
||||||
|
use CoreLibs\Convert\Html;
|
||||||
|
|
||||||
$log = new CoreLibs\Logging\Logging([
|
$log = new CoreLibs\Logging\Logging([
|
||||||
'log_folder' => BASE . LOG,
|
'log_folder' => BASE . LOG,
|
||||||
@@ -38,10 +39,12 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
|||||||
print "LOGFILE NAME: " . $log->getLogFile() . "<br>";
|
print "LOGFILE NAME: " . $log->getLogFile() . "<br>";
|
||||||
print "LOGFILE ID: " . $log->getLogFileId() . "<br>";
|
print "LOGFILE ID: " . $log->getLogFileId() . "<br>";
|
||||||
|
|
||||||
print "Lookup Regex: <pre>" . ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
|
print "Lookup Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS) . "</pre>";
|
||||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_NAMED . "</pre>";
|
print "Lookup Numbered Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_LOOKUP_NUMBERED) . "</pre>";
|
||||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_QUESTION_MARK . "</pre>";
|
print "Replace Named Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_NAMED) . "</pre>";
|
||||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_NUMBERED . "</pre>";
|
print "Replace Question Mark Regex: <pre>"
|
||||||
|
. Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_QUESTION_MARK) . "</pre>";
|
||||||
|
print "Replace Numbered Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_NUMBERED) . "</pre>";
|
||||||
|
|
||||||
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
|
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
|
||||||
// $binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
|
// $binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
|
||||||
@@ -91,40 +94,63 @@ RETURNING
|
|||||||
some_binary
|
some_binary
|
||||||
SQL;
|
SQL;
|
||||||
|
|
||||||
print "[ALL] Convert: "
|
print "<b>[ALL] Convert</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|
||||||
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
||||||
$params = [':baz' => 'SETBAZ', ':bez' => 'SETBEZ', ':biz' => 'SETBIZ'];
|
$params = [':baz' => 'SETBAZ', ':bez' => 'SETBEZ', ':biz' => 'SETBIZ'];
|
||||||
print "[NO PARAMS] Convert: "
|
print "<b>[NO PARAMS] Convert</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|
||||||
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
||||||
$params = null;
|
$params = null;
|
||||||
print "[NO PARAMS] Convert: "
|
print "<b>[NO PARAMS] Convert</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|
||||||
$query = "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar <> :row_varchar";
|
$query = "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar <> :row_varchar";
|
||||||
$params = null;
|
$params = null;
|
||||||
print "[NO PARAMS] Convert: "
|
print "<b>[NO PARAMS] Convert</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|
||||||
$query = "SELECT row_varchar, row_varchar_literal, row_int, row_date FROM table_with_primary_key";
|
$query = "SELECT row_varchar, row_varchar_literal, row_int, row_date FROM table_with_primary_key";
|
||||||
$params = null;
|
$params = null;
|
||||||
print "[NO PARAMS] TEST: "
|
print "<b>[NO PARAMS] TEST</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|
||||||
print "[P-CONV]: "
|
$query = <<<SQL
|
||||||
|
UPDATE table_with_primary_key SET
|
||||||
|
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
|
||||||
|
WHERE
|
||||||
|
row_varchar = $1
|
||||||
|
SQL;
|
||||||
|
$params = [1];
|
||||||
|
print "<b>[All the same params] TEST</b>: "
|
||||||
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
|
. "<br>";
|
||||||
|
echo "<hr>";
|
||||||
|
|
||||||
|
$query = <<<SQL
|
||||||
|
SELECT row_varchar, row_varchar_literal, row_int, row_date
|
||||||
|
FROM table_with_primary_key
|
||||||
|
WHERE row_varchar = :row_varchar
|
||||||
|
SQL;
|
||||||
|
$params = [':row_varchar' => 1];
|
||||||
|
print "<b>[: param] TEST</b>: "
|
||||||
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||||
|
. "<br>";
|
||||||
|
echo "<hr>";
|
||||||
|
|
||||||
|
print "<b>[P-CONV]</b>: "
|
||||||
. Support::printAr(
|
. Support::printAr(
|
||||||
ConvertPlaceholder::updateParamList([
|
ConvertPlaceholder::updateParamList([
|
||||||
'original' => [
|
'original' => [
|
||||||
@@ -186,6 +212,13 @@ SQL,
|
|||||||
'params' => [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234],
|
'params' => [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234],
|
||||||
'direction' => 'pg',
|
'direction' => 'pg',
|
||||||
],
|
],
|
||||||
|
'b?' => [
|
||||||
|
'query' => <<<SQL
|
||||||
|
SELECT test FROM test_foo = ?
|
||||||
|
SQL,
|
||||||
|
'params' => [1234],
|
||||||
|
'direction' => 'pg',
|
||||||
|
],
|
||||||
'b:' => [
|
'b:' => [
|
||||||
'query' => <<<SQL
|
'query' => <<<SQL
|
||||||
INSERT INTO test_foo (
|
INSERT INTO test_foo (
|
||||||
@@ -220,7 +253,7 @@ foreach ($test_queries as $info => $data) {
|
|||||||
$query = $data['query'];
|
$query = $data['query'];
|
||||||
$params = $data['params'];
|
$params = $data['params'];
|
||||||
$direction = $data['direction'];
|
$direction = $data['direction'];
|
||||||
print "[$info] Convert: "
|
print "<b>[$info] Convert</b>: "
|
||||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params, $direction))
|
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params, $direction))
|
||||||
. "<br>";
|
. "<br>";
|
||||||
echo "<hr>";
|
echo "<hr>";
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ $db->dbExecParams(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$cuuid = $db->dbGetReturningExt('cuuid');
|
$cuuid = $db->dbGetReturningExt('cuuid');
|
||||||
print "INSERTED: $cuuid<br>";
|
print "INSERTED: " . print_r($cuuid, true) . "<br>";
|
||||||
print "LAST ERROR: " . $db->dbGetLastError(true) . "<br>";
|
print "LAST ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||||
|
|
||||||
// read back
|
// read back
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
|
|||||||
print "NO DB HANDLER<br>";
|
print "NO DB HANDLER<br>";
|
||||||
}
|
}
|
||||||
// REGEX for placeholder count
|
// REGEX for placeholder count
|
||||||
print "Placeholder regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
|
print "Placeholder lookup regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_NUMBERED . "</pre>";
|
||||||
|
|
||||||
// turn on debug replace for placeholders
|
// turn on debug replace for placeholders
|
||||||
$db->dbSetDebugReplacePlaceholder(true);
|
$db->dbSetDebugReplacePlaceholder(true);
|
||||||
@@ -148,6 +148,7 @@ RETURNING
|
|||||||
bigint_a, number_real, number_double, numeric_3,
|
bigint_a, number_real, number_double, numeric_3,
|
||||||
uuid_var
|
uuid_var
|
||||||
SQL;
|
SQL;
|
||||||
|
print "Placeholders: <pre>" . print_r($db->dbGetQueryParamPlaceholders($query_insert), true) . "<pre>";
|
||||||
$status = $db->dbExecParams($query_insert, $query_params);
|
$status = $db->dbExecParams($query_insert, $query_params);
|
||||||
echo "<b>*</b><br>";
|
echo "<b>*</b><br>";
|
||||||
echo "INSERT ALL COLUMN TYPES: "
|
echo "INSERT ALL COLUMN TYPES: "
|
||||||
@@ -326,6 +327,7 @@ SQL,
|
|||||||
) {
|
) {
|
||||||
print "RES: " . Support::prAr($res) . "<br>";
|
print "RES: " . Support::prAr($res) . "<br>";
|
||||||
}
|
}
|
||||||
|
print "PL: " . Support::PrAr($db->dbGetPlaceholderConverted()) . "<br>";
|
||||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||||
|
|
||||||
print "</body></html>";
|
print "</body></html>";
|
||||||
|
|||||||
@@ -4283,6 +4283,17 @@ class IO
|
|||||||
return $this->field_names[$pos] ?? false;
|
return $this->field_names[$pos] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all the $ placeholders
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public function dbGetQueryParamPlaceholders(string $query): array
|
||||||
|
{
|
||||||
|
return $this->db_functions->__dbGetQueryParams($query);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a field type for a field name or pos,
|
* Return a field type for a field name or pos,
|
||||||
* will return false if field is not found in list
|
* will return false if field is not found in list
|
||||||
|
|||||||
@@ -978,12 +978,12 @@ class PgSQL implements Interface\SqlFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count placeholder queries. $ only
|
* Get the all the $ params, unique list
|
||||||
*
|
*
|
||||||
* @param string $query
|
* @param string $query
|
||||||
* @return int
|
* @return array<string>
|
||||||
*/
|
*/
|
||||||
public function __dbCountQueryParams(string $query): int
|
public function __dbGetQueryParams(string $query): array
|
||||||
{
|
{
|
||||||
$matches = [];
|
$matches = [];
|
||||||
// regex for params: only stand alone $number allowed
|
// regex for params: only stand alone $number allowed
|
||||||
@@ -998,11 +998,22 @@ class PgSQL implements Interface\SqlFunctions
|
|||||||
// Matches in 1:, must be array_filtered to remove empty, count with array_unique
|
// Matches in 1:, must be array_filtered to remove empty, count with array_unique
|
||||||
// Regex located in the ConvertPlaceholder class
|
// Regex located in the ConvertPlaceholder class
|
||||||
preg_match_all(
|
preg_match_all(
|
||||||
ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS,
|
ConvertPlaceholder::REGEX_LOOKUP_NUMBERED,
|
||||||
$query,
|
$query,
|
||||||
$matches
|
$matches
|
||||||
);
|
);
|
||||||
return count(array_unique(array_filter($matches[3])));
|
return array_unique(array_filter($matches[ConvertPlaceholder::MATCHING_POS]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count placeholder queries. $ only
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function __dbCountQueryParams(string $query): int
|
||||||
|
{
|
||||||
|
return count($this->__dbGetQueryParams($query));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,76 +14,57 @@ namespace CoreLibs\DB\Support;
|
|||||||
|
|
||||||
class ConvertPlaceholder
|
class ConvertPlaceholder
|
||||||
{
|
{
|
||||||
// NOTE for missing: range */+ are not iplemented in the regex below, but - is for now
|
/** @var string text block in SQL, single quited
|
||||||
// NOTE some combinations are allowed, but the query will fail before this
|
* Note that does not include $$..$$ strings or anything with token name or nested ones
|
||||||
/** @var string split regex, entries before $ group */
|
*/
|
||||||
private const PATTERN_QUERY_SPLIT =
|
private const PATTERN_TEXT_BLOCK_SINGLE_QUOTE = '(?:\'(?:[^\'\\\\]|\\\\.)*\')';
|
||||||
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
|
/** @var string text block in SQL, dollar quoted
|
||||||
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
|
* NOTE: if this is added everything shifts by one lookup number
|
||||||
. '[<>=]|' // general set for <, >, = in any query with any combination
|
*/
|
||||||
. '\^@|' // text search for start from text with ^@
|
private const PATTERN_TEXT_BLOCK_DOLLAR = '(?:\$([^$]*)\$.*?\$\1\$)';
|
||||||
. '\|\||' // concats two elements
|
|
||||||
. '&&|' // array overlap
|
|
||||||
. '\-\|\-|' // range overlap for array
|
|
||||||
. '[^-]-{1}|' // single -, used in JSON too
|
|
||||||
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-|' // JSON searches, Array searchs, etc
|
|
||||||
. 'THEN|ELSE' // command parts (CASE)
|
|
||||||
;
|
|
||||||
/** @var string the main regex including the pattern query split */
|
|
||||||
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
|
|
||||||
/** @var string comment regex
|
/** @var string comment regex
|
||||||
* anything that starts with -- and ends with a line break but any character that is not line break inbetween */
|
* anything that starts with -- and ends with a line break but any character that is not line break inbetween
|
||||||
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)*\s*';
|
* this is the FIRST thing in the line and will skip any further lookups */
|
||||||
/** @var string parts to ignore in the SQL */
|
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)';
|
||||||
private const PATTERN_IGNORE =
|
// below are the params lookups
|
||||||
// digit -> ignore
|
/** @var string named parameters, must start with single : */
|
||||||
'\d+|'
|
private const PATTERN_NAMED = '((?<!:):(?:\w+))';
|
||||||
// other string -> ignore
|
/** @var string question mark parameters, will catch any */
|
||||||
. '(?:\'.*?\')|';
|
private const PATTERN_QUESTION_MARK = '(\?{1})';
|
||||||
/** @var string named parameters */
|
/** @var string numbered parameters, can only start 1 to 9, second and further digits can be 0-9
|
||||||
private const PATTERN_NAMED = '(:\w+)';
|
* This ignores the $$ ... $$ escape syntax. If we find something like this will fail
|
||||||
/** @var string question mark parameters */
|
* It is recommended to use proper string escape quiting for writing data to the DB
|
||||||
private const PATTERN_QUESTION_MARK = '(?:(?:\?\?)?\s*(\?{1}))';
|
*/
|
||||||
/** @var string numbered parameters */
|
|
||||||
private const PATTERN_NUMBERED = '(\$[1-9]{1}(?:[0-9]{1,})?)';
|
private const PATTERN_NUMBERED = '(\$[1-9]{1}(?:[0-9]{1,})?)';
|
||||||
// below here are full regex that will be used
|
// below here are full regex that will be used
|
||||||
/** @var string replace regex for named (:...) entries */
|
/** @var string replace regex for named (:...) entries */
|
||||||
public const REGEX_REPLACE_NAMED = '/'
|
public const REGEX_REPLACE_NAMED = '/'
|
||||||
. '(' . self::PATTERN_ELEMENT . ')'
|
. self::PATTERN_COMMENT . '|'
|
||||||
. self::PATTERN_COMMENT
|
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||||
. '('
|
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||||
. self::PATTERN_IGNORE
|
|
||||||
. self::PATTERN_NAMED
|
. self::PATTERN_NAMED
|
||||||
. ')'
|
|
||||||
. '/s';
|
. '/s';
|
||||||
/** @var string replace regex for question mark (?) entries */
|
/** @var string replace regex for question mark (?) entries */
|
||||||
public const REGEX_REPLACE_QUESTION_MARK = '/'
|
public const REGEX_REPLACE_QUESTION_MARK = '/'
|
||||||
. '(' . self::PATTERN_ELEMENT . ')'
|
. self::PATTERN_COMMENT . '|'
|
||||||
. self::PATTERN_COMMENT
|
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||||
. '('
|
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||||
. self::PATTERN_IGNORE
|
|
||||||
. self::PATTERN_QUESTION_MARK
|
. self::PATTERN_QUESTION_MARK
|
||||||
. ')'
|
|
||||||
. '/s';
|
. '/s';
|
||||||
/** @var string replace regex for numbered ($n) entries */
|
/** @var string replace regex for numbered ($n) entries */
|
||||||
public const REGEX_REPLACE_NUMBERED = '/'
|
public const REGEX_REPLACE_NUMBERED = '/'
|
||||||
. '(' . self::PATTERN_ELEMENT . ')'
|
. self::PATTERN_COMMENT . '|'
|
||||||
. self::PATTERN_COMMENT
|
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||||
. '('
|
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||||
. self::PATTERN_IGNORE
|
|
||||||
. self::PATTERN_NUMBERED
|
. self::PATTERN_NUMBERED
|
||||||
. ')'
|
|
||||||
. '/s';
|
. '/s';
|
||||||
/** @var string the main lookup query for all placeholders */
|
/** @var string the main lookup query for all placeholders */
|
||||||
public const REGEX_LOOKUP_PLACEHOLDERS = '/'
|
public const REGEX_LOOKUP_PLACEHOLDERS = '/'
|
||||||
// prefix string part, must match towards
|
. self::PATTERN_COMMENT . '|'
|
||||||
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
|
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||||
. self::PATTERN_ELEMENT
|
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||||
. self::PATTERN_COMMENT
|
|
||||||
// match for replace part
|
// match for replace part
|
||||||
. '(?:'
|
. '(?:'
|
||||||
// ignore parts
|
|
||||||
. self::PATTERN_IGNORE
|
|
||||||
// :name named part (PDO) [1]
|
// :name named part (PDO) [1]
|
||||||
. self::PATTERN_NAMED . '|'
|
. self::PATTERN_NAMED . '|'
|
||||||
// ? question mark part (PDO) [2]
|
// ? question mark part (PDO) [2]
|
||||||
@@ -94,6 +75,26 @@ class ConvertPlaceholder
|
|||||||
. ')'
|
. ')'
|
||||||
// single line -> add line break to matches in "."
|
// single line -> add line break to matches in "."
|
||||||
. '/s';
|
. '/s';
|
||||||
|
/** @var string lookup for only numbered placeholders */
|
||||||
|
public const REGEX_LOOKUP_NUMBERED = '/'
|
||||||
|
. self::PATTERN_COMMENT . '|'
|
||||||
|
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||||
|
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||||
|
// match for replace part
|
||||||
|
. '(?:'
|
||||||
|
// $n numbered part (\PG php) [1]
|
||||||
|
. self::PATTERN_NUMBERED
|
||||||
|
// end match
|
||||||
|
. ')'
|
||||||
|
. '/s';
|
||||||
|
/** @var int position for regex in full placeholder lookup: named */
|
||||||
|
public const LOOOKUP_NAMED_POS = 2;
|
||||||
|
/** @var int position for regex in full placeholder lookup: question mark */
|
||||||
|
public const LOOOKUP_QUESTION_MARK_POS = 3;
|
||||||
|
/** @var int position for regex in full placeholder lookup: numbered */
|
||||||
|
public const LOOOKUP_NUMBERED_POS = 4;
|
||||||
|
/** @var int matches position for replacement and single lookup */
|
||||||
|
public const MATCHING_POS = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert PDO type query with placeholders to \PG style and vica versa
|
* Convert PDO type query with placeholders to \PG style and vica versa
|
||||||
@@ -132,11 +133,12 @@ class ConvertPlaceholder
|
|||||||
$found = -1;
|
$found = -1;
|
||||||
}
|
}
|
||||||
/** @var array<string> 1: named */
|
/** @var array<string> 1: named */
|
||||||
$named_matches = array_filter($matches[1]);
|
$named_matches = array_filter($matches[self::LOOOKUP_NAMED_POS]);
|
||||||
/** @var array<string> 2: open ? */
|
/** @var array<string> 2: open ? */
|
||||||
$qmark_matches = array_filter($matches[2]);
|
$qmark_matches = array_filter($matches[self::LOOOKUP_QUESTION_MARK_POS]);
|
||||||
/** @var array<string> 3: $n matches */
|
/** @var array<string> 3: $n matches */
|
||||||
$numbered_matches = array_filter($matches[3]);
|
$numbered_matches = array_filter($matches[self::LOOOKUP_NUMBERED_POS]);
|
||||||
|
// print "**MATCHES**: <pre>" . print_r($matches, true) . "</pre>";
|
||||||
// count matches
|
// count matches
|
||||||
$count_named = count(array_unique($named_matches));
|
$count_named = count(array_unique($named_matches));
|
||||||
$count_qmark = count($qmark_matches);
|
$count_qmark = count($qmark_matches);
|
||||||
@@ -235,38 +237,37 @@ class ConvertPlaceholder
|
|||||||
$empty_params = $converted_placeholders['original']['empty_params'];
|
$empty_params = $converted_placeholders['original']['empty_params'];
|
||||||
switch ($converted_placeholders['type']) {
|
switch ($converted_placeholders['type']) {
|
||||||
case 'named':
|
case 'named':
|
||||||
// 0: full
|
// 1: replace part :named
|
||||||
// 0: full
|
|
||||||
// 1: pre part
|
|
||||||
// 2: keep part UNLESS '3' is set
|
|
||||||
// 3: replace part :named
|
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
$query_new = preg_replace_callback(
|
$query_new = preg_replace_callback(
|
||||||
self::REGEX_REPLACE_NAMED,
|
self::REGEX_REPLACE_NAMED,
|
||||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
||||||
// only count up if $match[3] is not yet in lookup table
|
if (!isset($matches[self::MATCHING_POS])) {
|
||||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
throw new \RuntimeException(
|
||||||
|
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||||
|
209
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$match = $matches[self::MATCHING_POS];
|
||||||
|
// only count up if $match[1] is not yet in lookup table
|
||||||
|
if (empty($params_lookup[$match])) {
|
||||||
$pos++;
|
$pos++;
|
||||||
$params_lookup[$matches[3]] = '$' . $pos;
|
$params_lookup[$match] = '$' . $pos;
|
||||||
// skip params setup if param list is empty
|
// skip params setup if param list is empty
|
||||||
if (!$empty_params) {
|
if (!$empty_params) {
|
||||||
$params_new[] = $params[$matches[3]] ??
|
$params_new[] = $params[$match] ??
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
'Cannot lookup ' . $matches[3] . ' in params list',
|
'Cannot lookup ' . $match . ' in params list',
|
||||||
210
|
210
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||||
return $matches[1] . (
|
return $params_lookup[$match] ??
|
||||||
empty($matches[3]) ?
|
throw new \RuntimeException(
|
||||||
$matches[2] :
|
'Cannot lookup ' . $match . ' in params lookup list',
|
||||||
$params_lookup[$matches[3]] ??
|
211
|
||||||
throw new \RuntimeException(
|
);
|
||||||
'Cannot lookup ' . $matches[3] . ' in params lookup list',
|
|
||||||
211
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
$converted_placeholders['original']['query']
|
$converted_placeholders['original']['query']
|
||||||
);
|
);
|
||||||
@@ -276,61 +277,61 @@ class ConvertPlaceholder
|
|||||||
// order and data stays the same
|
// order and data stays the same
|
||||||
$params_new = $params ?? [];
|
$params_new = $params ?? [];
|
||||||
}
|
}
|
||||||
// 0: full
|
// 1: replace part ?
|
||||||
// 1: pre part
|
|
||||||
// 2: keep part UNLESS '3' is set
|
|
||||||
// 3: replace part ?
|
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
$query_new = preg_replace_callback(
|
$query_new = preg_replace_callback(
|
||||||
self::REGEX_REPLACE_QUESTION_MARK,
|
self::REGEX_REPLACE_QUESTION_MARK,
|
||||||
function ($matches) use (&$pos, &$params_lookup) {
|
function ($matches) use (&$pos, &$params_lookup) {
|
||||||
|
if (!isset($matches[self::MATCHING_POS])) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||||
|
229
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$match = $matches[self::MATCHING_POS];
|
||||||
// only count pos up for actual replacements we will do
|
// only count pos up for actual replacements we will do
|
||||||
if (!empty($matches[3])) {
|
if (!empty($match)) {
|
||||||
$pos++;
|
$pos++;
|
||||||
$params_lookup[] = '$' . $pos;
|
$params_lookup[] = '$' . $pos;
|
||||||
}
|
}
|
||||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||||
return $matches[1] . (
|
return '$' . $pos;
|
||||||
empty($matches[3]) ?
|
|
||||||
$matches[2] :
|
|
||||||
'$' . $pos
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
$converted_placeholders['original']['query']
|
$converted_placeholders['original']['query']
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'numbered':
|
case 'numbered':
|
||||||
// 0: full
|
// 1: replace part $numbered
|
||||||
// 1: pre part
|
|
||||||
// 2: keep part UNLESS '3' is set
|
|
||||||
// 3: replace part $numbered
|
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
$query_new = preg_replace_callback(
|
$query_new = preg_replace_callback(
|
||||||
self::REGEX_REPLACE_NUMBERED,
|
self::REGEX_REPLACE_NUMBERED,
|
||||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
||||||
// only count up if $match[3] is not yet in lookup table
|
if (!isset($matches[self::MATCHING_POS])) {
|
||||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
throw new \RuntimeException(
|
||||||
|
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||||
|
239
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$match = $matches[self::MATCHING_POS];
|
||||||
|
// only count up if $match[1] is not yet in lookup table
|
||||||
|
if (empty($params_lookup[$match])) {
|
||||||
$pos++;
|
$pos++;
|
||||||
$params_lookup[$matches[3]] = ':' . $pos . '_named';
|
$params_lookup[$match] = ':' . $pos . '_named';
|
||||||
// skip params setup if param list is empty
|
// skip params setup if param list is empty
|
||||||
if (!$empty_params) {
|
if (!$empty_params) {
|
||||||
$params_new[] = $params[($pos - 1)] ??
|
$params_new[] = $params[($pos - 1)] ??
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
'Cannot lookup ' . ($pos - 1) . ' in params list',
|
'Cannot lookup ' . ($pos - 1) . ' in params list',
|
||||||
220
|
230
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||||
return $matches[1] . (
|
return $params_lookup[$match] ??
|
||||||
empty($matches[3]) ?
|
throw new \RuntimeException(
|
||||||
$matches[2] :
|
'Cannot lookup ' . $match . ' in params lookup list',
|
||||||
$params_lookup[$matches[3]] ??
|
231
|
||||||
throw new \RuntimeException(
|
);
|
||||||
'Cannot lookup ' . $matches[3] . ' in params lookup list',
|
|
||||||
221
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
$converted_placeholders['original']['query']
|
$converted_placeholders['original']['query']
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user