Compare commits

...

3 Commits

Author SHA1 Message Date
Clemens Schwaighofer
4bbbd653cd DB\IO fix for regex query detection
Fix for basic query detection:
Simeple starts with
SELECT/WITH/SHOW
INSERT INTO/UPDATE/DELETE FROM
UPDATE

Above does no complex query detection, just if the string starts with this

Fix form table detection for primary key auto set trial.
2023-01-27 11:12:46 +09:00
Clemens Schwaighofer
4c28e6d0ec Change DB\IO Returning check regex
Better regex to only get last returning in query, with proper parameter
check
2023-01-26 16:37:22 +09:00
Clemens Schwaighofer
66b7e81463 Bug fix in DB\IO for EOM string build queries with returning
on EOM string build queries it was not checked that RETURNING could have
no space in front.

Fixed and updated test calls
2023-01-25 16:47:31 +09:00
4 changed files with 313 additions and 89 deletions

View File

@@ -155,37 +155,57 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExec("DROP TABLE table_without_primary_key"); $db->dbExec("DROP TABLE table_without_primary_key");
$db->dbExec("DROP TABLE test_meta"); $db->dbExec("DROP TABLE test_meta");
} }
$base_table = "uid VARCHAR, " // uid is for internal reference tests // uid is for internal reference tests
. "row_int INT, " $base_table = <<<EOM
. "row_numeric NUMERIC, " uid VARCHAR,
. "row_varchar VARCHAR, " row_int INT,
. "row_varchar_literal VARCHAR, " row_numeric NUMERIC,
. "row_json JSON, " row_varchar VARCHAR,
. "row_jsonb JSONB, " row_varchar_literal VARCHAR,
. "row_bytea BYTEA, " row_json JSON,
. "row_timestamp TIMESTAMP WITHOUT TIME ZONE, " row_jsonb JSONB,
. "row_date DATE, " row_bytea BYTEA,
. "row_interval INTERVAL, " row_timestamp TIMESTAMP WITHOUT TIME ZONE,
. "row_array_int INT ARRAY, " row_date DATE,
. "row_array_varchar VARCHAR ARRAY" row_interval INTERVAL,
. ") WITHOUT OIDS"; row_array_int INT ARRAY,
row_array_varchar VARCHAR ARRAY
)
WITHOUT OIDS
EOM;
// create the tables // create the tables
$db->dbExec( $db->dbExec(
"CREATE TABLE table_with_primary_key (" // primary key name is table + '_id'
<<<EOM
CREATE TABLE table_with_primary_key (
table_with_primary_key_id SERIAL PRIMARY KEY,
$base_table
EOM
/* "CREATE TABLE table_with_primary_key ("
// primary key name is table + '_id' // primary key name is table + '_id'
. "table_with_primary_key_id SERIAL PRIMARY KEY, " . "table_with_primary_key_id SERIAL PRIMARY KEY, "
. $base_table . $base_table */
); );
$db->dbExec( $db->dbExec(
"CREATE TABLE table_without_primary_key (" <<<EOM
. $base_table CREATE TABLE table_without_primary_key (
$base_table
EOM
/* "CREATE TABLE table_without_primary_key ("
. $base_table */
); );
// create simple table for meta test // create simple table for meta test
$db->dbExec( $db->dbExec(
"CREATE TABLE test_meta (" <<<EOM
CREATE TABLE test_meta (
row_1 VARCHAR,
row_2 INT
) WITHOUT OIDS
EOM
/* "CREATE TABLE test_meta ("
. "row_1 VARCHAR, " . "row_1 VARCHAR, "
. "row_2 INT" . "row_2 INT"
. ") WITHOUT OIDS" . ") WITHOUT OIDS" */
); );
// set some test schema // set some test schema
$db->dbExec("CREATE SCHEMA IF NOT EXISTS testschema"); $db->dbExec("CREATE SCHEMA IF NOT EXISTS testschema");
@@ -3893,6 +3913,38 @@ final class CoreLibsDBIOTest extends TestCase
] ]
] ]
], ],
// same but as EOM
'single insert (PK), EOM string' => [
<<<EOM
INSERT INTO table_with_primary_key (
row_varchar, row_varchar_literal, row_int, row_date
) VALUES (
'Text', 'Other', 123, '2022-03-01'
)
RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM,
null,
null,
null,
[
'row_varchar' => 'Text',
'row_varchar_literal' => 'Other',
'row_int' => 123,
'row_date' => '2022-03-01',
// 'table_with_primary_key_id' => "/^\d+$/",
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
],
[
0 => [
'row_varchar' => 'Text',
'row_varchar_literal' => 'Other',
'row_int' => 123,
'row_date' => '2022-03-01',
// 'table_with_primary_key_id' => "/^\d+$/",
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
]
]
],
// double insert (PK) // double insert (PK)
'dobule insert (PK)' => [ 'dobule insert (PK)' => [
"INSERT INTO table_with_primary_key " "INSERT INTO table_with_primary_key "
@@ -3910,14 +3962,14 @@ final class CoreLibsDBIOTest extends TestCase
'row_varchar_literal' => 'Other', 'row_varchar_literal' => 'Other',
'row_int' => 123, 'row_int' => 123,
'row_date' => '2022-03-01', 'row_date' => '2022-03-01',
'table_with_primary_key_id' => $table_with_primary_key_id + 2, 'table_with_primary_key_id' => $table_with_primary_key_id + 3,
], ],
1 => [ 1 => [
'row_varchar' => 'Foxtrott', 'row_varchar' => 'Foxtrott',
'row_varchar_literal' => 'Tango', 'row_varchar_literal' => 'Tango',
'row_int' => 789, 'row_int' => 789,
'row_date' => '1982-10-15', 'row_date' => '1982-10-15',
'table_with_primary_key_id' => $table_with_primary_key_id + 3, 'table_with_primary_key_id' => $table_with_primary_key_id + 4,
], ],
], ],
[ [
@@ -3926,14 +3978,14 @@ final class CoreLibsDBIOTest extends TestCase
'row_varchar_literal' => 'Other', 'row_varchar_literal' => 'Other',
'row_int' => 123, 'row_int' => 123,
'row_date' => '2022-03-01', 'row_date' => '2022-03-01',
'table_with_primary_key_id' => $table_with_primary_key_id + 2, 'table_with_primary_key_id' => $table_with_primary_key_id + 3,
], ],
1 => [ 1 => [
'row_varchar' => 'Foxtrott', 'row_varchar' => 'Foxtrott',
'row_varchar_literal' => 'Tango', 'row_varchar_literal' => 'Tango',
'row_int' => 789, 'row_int' => 789,
'row_date' => '1982-10-15', 'row_date' => '1982-10-15',
'table_with_primary_key_id' => $table_with_primary_key_id + 3, 'table_with_primary_key_id' => $table_with_primary_key_id + 4,
], ],
] ]
], ],
@@ -3961,7 +4013,35 @@ final class CoreLibsDBIOTest extends TestCase
'row_date' => '2022-03-01', 'row_date' => '2022-03-01',
] ]
] ]
] ],
// same as above but as EOM string
'single insert (No PK), EOM string' => [
<<<EOM
INSERT INTO table_without_primary_key (
row_varchar, row_varchar_literal, row_int, row_date
) VALUES (
'Text', 'Other', 123, '2022-03-01'
)
RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM,
null,
null,
null,
[
'row_varchar' => 'Text',
'row_varchar_literal' => 'Other',
'row_int' => 123,
'row_date' => '2022-03-01',
],
[
0 => [
'row_varchar' => 'Text',
'row_varchar_literal' => 'Other',
'row_int' => 123,
'row_date' => '2022-03-01',
]
]
],
]; ];
} }
@@ -4008,11 +4088,13 @@ final class CoreLibsDBIOTest extends TestCase
$this->assertEquals( $this->assertEquals(
$expected_ret_ext, $expected_ret_ext,
$returning_ext $returning_ext,
'Returning extended failed'
); );
$this->assertEquals( $this->assertEquals(
$expected_ret_arr, $expected_ret_arr,
$returning_arr $returning_arr,
'Returning Array failed'
); );
// print "EXT: " . print_r($returning_ext, true) . "\n"; // print "EXT: " . print_r($returning_ext, true) . "\n";

View File

@@ -1,103 +1,128 @@
# Upgrade to Version 6 # Upgrade to Version 6
* remove old `lib/CoreLibs` and copy the new over * remove old `lib/CoreLibs` and copy the new over
* copy `config/config.php` * copy `config/config.php`
* install composer if not installed `composer init` and `composer install` * install composer if not installed `composer init` and `composer install`
* update composer.json * update composer.json
```json
```json
"autoload": { "autoload": {
"classmap": [ "classmap": [
"lib/" "lib/"
] ]
}, },
``` ```
Run to update autoloader list Run to update autoloader list
```sh ```sh
composer dump-autoload composer dump-autoload
``` ```
* copy `includes/edit_base.inc` * copy `includes/edit_base.inc`
* add session start in the top header block where the `header()` calls are * add session start in the top header block where the `header()` calls are
```php ```php
// start session // start session
CoreLibs\Create\Session::startSession(); CoreLibs\Create\Session::startSession();
``` ```
* update all header calls if needed to add new log type call
```php * update all header calls if needed to add new log type call
```php
// create logger // create logger
$log = new CoreLibs\Debug\Logging([ $log = new CoreLibs\Debug\Logging([
'log_folder' => BASE . LOG, 'log_folder' => BASE . LOG,
'file_id' => LOG_FILE_ID, 'file_id' => LOG_FILE_ID,
'print_file_date' => true, 'print_file_date' => true,
'debug_all' => $DEBUG_ALL ?? false, 'debug_all' => $DEBUG_ALL ?? false,
'echo_all' => $ECHO_ALL ?? false, 'echo_all' => $ECHO_ALL ?? false,
'print_all' => $PRINT_ALL ?? false, 'print_all' => $PRINT_ALL ?? false,
]); ]);
``` ```
* add a db class
* add a db class
```php ```php
// db config with logger // db config with logger
$db = new CoreLibs\DB\IO(DB_CONFIG, $log); $db = new CoreLibs\DB\IO(DB_CONFIG, $log);
``` ```
* login class needs to have db and logger added
* login class needs to have db and logger added
```php ```php
// login & page access check // login & page access check
$login = new CoreLibs\ACL\Login($db, $log); $login = new CoreLibs\ACL\Login($db, $log);
``` ```
* update language class * update language class
```php ```php
// pre auto detect language after login // pre auto detect language after login
$locale = \CoreLibs\Language\GetLocale::setLocale(); $locale = \CoreLibs\Language\GetLocale::setLocale();
// set lang and pass to smarty/backend // set lang and pass to smarty/backend
$l10n = new \CoreLibs\Language\L10n( $l10n = new \CoreLibs\Language\L10n(
$locale['locale'], $locale['locale'],
$locale['domain'], $locale['domain'],
$locale['path'], $locale['path'],
); );
``` ```
* smarty needs language * smarty needs language
```php ```php
$smarty = new CoreLibs\Template\SmartyExtend($l10n, $locale); $smarty = new CoreLibs\Template\SmartyExtend($l10n, $locale);
``` ```
* admin backend also needs logger * admin backend also needs logger
```php ```php
$cms = new CoreLibs\Admin\Backend($db, $log, $l10n, $locale); $cms = new CoreLibs\Admin\Backend($db, $log, $l10n, $locale);
``` ```
* update and `$cms` or similar calls so db is in `$cms->db->...` and log are in `$cms->log->...` * update and `$cms` or similar calls so db is in `$cms->db->...` and log are in `$cms->log->...`
* update all `config.*.php` files where needed * update all `config.*.php` files where needed
* check config.master.php for `BASE_NAME` and `G_TITLE` and set them in the `.env` file so the `config.master.php` can be copied as os * check config.master.php for `BASE_NAME` and `G_TITLE` and set them in the `.env` file so the `config.master.php` can be copied as os
* If not doable, see changed below in `config.master.php` must remove old auto loder and `FLASH` constant at least * If not doable, see changed below in `config.master.php` must remove old auto loder and `FLASH` constant at least
**REMOVE:** **REMOVE:**
```php ```php
/************* AUTO LOADER *******************/ /************* AUTO LOADER *******************/
// read auto loader // read auto loader
require BASE . LIB . 'autoloader.php'; require BASE . LIB . 'autoloader.php';
``` ```
**UPDATE:** **UPDATE:**
```php ```php
// po langs [DEPRECAED: use LOCALE] // po langs [DEPRECAED: use LOCALE]
define('LANG', 'lang' . DIRECTORY_SEPARATOR); define('LANG', 'lang' . DIRECTORY_SEPARATOR);
// po locale file // po locale file
define('LOCALE', 'locale' . DIRECTORY_SEPARATOR); define('LOCALE', 'locale' . DIRECTORY_SEPARATOR);
``` ```
```php ```php
// SSL host name // SSL host name
// define('SSL_HOST', $_ENV['SSL_HOST'] ?? ''); // define('SSL_HOST', $_ENV['SSL_HOST'] ?? '');
``` ```
```php ```php
// define full regex // define full regex
define('PASSWORD_REGEX', "/^" define('PASSWORD_REGEX', "/^"
. (defined('PASSWORD_LOWER') ? PASSWORD_LOWER : '') . (defined('PASSWORD_LOWER') ? PASSWORD_LOWER : '')
. (defined('PASSWORD_UPPER') ? PASSWORD_UPPER : '') . (defined('PASSWORD_UPPER') ? PASSWORD_UPPER : '')
. (defined('PASSWORD_NUMBER') ? PASSWORD_NUMBER : '') . (defined('PASSWORD_NUMBER') ? PASSWORD_NUMBER : '')
. (defined('PASSWORD_SPECIAL') ? PASSWORD_SPECIAL : '') . (defined('PASSWORD_SPECIAL') ? PASSWORD_SPECIAL : '')
. "[A-Za-z\d" . PASSWORD_SPECIAL_RANGE . "]{" . PASSWORD_MIN_LENGTH . "," . PASSWORD_MAX_LENGTH . "}$/"); . "[A-Za-z\d" . PASSWORD_SPECIAL_RANGE . "]{" . PASSWORD_MIN_LENGTH . "," . PASSWORD_MAX_LENGTH . "}$/");
``` ```
```php ```php
/************* LAYOUT WIDTHS *************/ /************* LAYOUT WIDTHS *************/
define('PAGE_WIDTH', '100%'); define('PAGE_WIDTH', '100%');
define('CONTENT_WIDTH', '100%'); define('CONTENT_WIDTH', '100%');
``` ```
```php ```php
/************* OVERALL CONTROL NAMES *************/ /************* OVERALL CONTROL NAMES *************/
// BELOW has HAS to be changed // BELOW has HAS to be changed
@@ -105,6 +130,7 @@ define('CONTENT_WIDTH', '100%');
// only alphanumeric characters, strip all others // only alphanumeric characters, strip all others
define('BASE_NAME', preg_replace('/[^A-Za-z0-9]/', '', $_ENV['BASE_NAME'] ?? '')); define('BASE_NAME', preg_replace('/[^A-Za-z0-9]/', '', $_ENV['BASE_NAME'] ?? ''));
``` ```
```php ```php
/************* LANGUAGE / ENCODING *******/ /************* LANGUAGE / ENCODING *******/
// default lang + encoding // default lang + encoding
@@ -112,53 +138,63 @@ define('DEFAULT_LOCALE', 'en_US.UTF-8');
// default web page encoding setting // default web page encoding setting
define('DEFAULT_ENCODING', 'UTF-8'); define('DEFAULT_ENCODING', 'UTF-8');
``` ```
```php ```php
// BAIL ON MISSING DB CONFIG: // BAIL ON MISSING DB CONFIG:
// we have either no db selction for this host but have db config entries // we have either no db selction for this host but have db config entries
// or we have a db selection but no db config as array or empty // or we have a db selection but no db config as array or empty
// or we have a selection but no matching db config entry // or we have a selection but no matching db config entry
if ( if (
(!isset($SITE_CONFIG[HOST_NAME]['db_host']) && count($DB_CONFIG)) || (!isset($SITE_CONFIG[HOST_NAME]['db_host']) && count($DB_CONFIG)) ||
(isset($SITE_CONFIG[HOST_NAME]['db_host']) && (isset($SITE_CONFIG[HOST_NAME]['db_host']) &&
// missing DB CONFIG // missing DB CONFIG
((is_array($DB_CONFIG) && !count($DB_CONFIG)) || ((is_array($DB_CONFIG) && !count($DB_CONFIG)) ||
!is_array($DB_CONFIG) || !is_array($DB_CONFIG) ||
// has DB CONFIG but no match // has DB CONFIG but no match
empty($DB_CONFIG[$SITE_CONFIG[HOST_NAME]['db_host']])) empty($DB_CONFIG[$SITE_CONFIG[HOST_NAME]['db_host']]))
) )
) { ) {
echo 'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator'; echo 'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator';
exit; exit;
} }
``` ```
```php ```php
// remove SITE_LANG // remove SITE_LANG
define('SITE_LOCALE', $SITE_CONFIG[HOST_NAME]['site_locale'] ?? DEFAULT_LOCALE); define('SITE_LOCALE', $SITE_CONFIG[HOST_NAME]['site_locale'] ?? DEFAULT_LOCALE);
define('SITE_ENCODING', $SITE_CONFIG[HOST_NAME]['site_encoding'] ?? DEFAULT_ENCODING); define('SITE_ENCODING', $SITE_CONFIG[HOST_NAME]['site_encoding'] ?? DEFAULT_ENCODING);
``` ```
```php ```php
/************* GENERAL PAGE TITLE ********/ /************* GENERAL PAGE TITLE ********/
define('G_TITLE', $_ENV['G_TITLE'] ?? ''); define('G_TITLE', $_ENV['G_TITLE'] ?? '');
``` ```
* move all login passweords into the `.env` file in the `configs/` folder * move all login passweords into the `.env` file in the `configs/` folder
in the `.env` file in the `.env` file
```
```sql
DB_NAME.TEST=some_database DB_NAME.TEST=some_database
... ...
``` ```
In the config then In the config then
```php ```php
'db_name' => $_ENV['DB_NAME.TEST'] ?? '', 'db_name' => $_ENV['DB_NAME.TEST'] ?? '',
``` ```
* config.host.php update * config.host.php update
must add site_locale (site_lang + site_encoding) must add site_locale (site_lang + site_encoding)
remove site_lang remove site_lang
```php ```php
// lang + encoding // lang + encoding
'site_locale' => 'en_US.UTF-8', 'site_locale' => 'en_US.UTF-8',
// site language // site language
'site_encoding' => 'UTF-8', 'site_encoding' => 'UTF-8',
``` ```
* copy `layout/admin/javascript/edit.jq.js` * copy `layout/admin/javascript/edit.jq.js`
* check other javacsript files if needed (`edit.jq.js`) * check other javacsript files if needed (`edit.jq.js`)

View File

@@ -118,7 +118,8 @@ print "<b>TRUNCATE test_foobar</b><br>";
$query = "TRUNCATE test_foobar"; $query = "TRUNCATE test_foobar";
$db->dbExec($query); $db->dbExec($query);
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO TEST " . time() . "') RETURNING test"); $status = $db->dbExec("INSERT INTO test_foo (test, number_a) VALUES "
. "('FOO TEST " . time() . "', 1) RETURNING test, number_a");
print "DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>" print "DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>" . "QUERY: " . $db->dbGetQuery() . " |<br>"
. "DB OBJECT: <pre>" . print_r($status, true) . "</pre>| " . "DB OBJECT: <pre>" . print_r($status, true) . "</pre>| "
@@ -127,6 +128,29 @@ print "DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | " . "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>"; . "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
var_dump($db->dbGetReturningExt());
// same as above but use an EOM string
$some_time = time();
$query = <<<EOM
INSERT INTO test_foo (
test, number_a
) VALUES (
'EOM FOO TEST $some_time', 1
)
RETURNING test, number_a
EOM;
$status = $db->dbExec($query);
print "EOM STRING DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "DB OBJECT: <pre>" . print_r($status, true) . "</pre>| "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
var_dump($db->dbGetReturningExt());
// should throw deprecated error // should throw deprecated error
// $db->getReturningExt(); // $db->getReturningExt();
print "DIRECT INSERT PREVIOUS INSERTED: " print "DIRECT INSERT PREVIOUS INSERTED: "
@@ -137,7 +161,7 @@ print "DIRECT INSERT PREVIOUS INSERTED: "
$db->dbPrepare("ins_test_foo", "INSERT INTO test_foo (test) VALUES ($1) RETURNING test"); $db->dbPrepare("ins_test_foo", "INSERT INTO test_foo (test) VALUES ($1) RETURNING test");
$status = $db->dbExecute("ins_test_foo", ['BAR TEST ' . time()]); $status = $db->dbExecute("ins_test_foo", ['BAR TEST ' . time()]);
print "PREPARE INSERT[ins_test_foo] STATUS: " . Support::printToString($status) . " |<br>" print "PREPARE INSERT[ins_test_foo] STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>" . "QUERY: " . $db->dbGetPrepareCursorValue('ins_test_foo', 'query') . " |<br>"
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | " . "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | " . "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "<br>"; . "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "<br>";
@@ -150,6 +174,23 @@ print "PREPARE CURSOR RETURN:<br>";
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) { foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
print "KEY: " . $key . ': ' . $db->dbGetPrepareCursorValue('ins_test_foo', $key) . "<br>"; print "KEY: " . $key . ': ' . $db->dbGetPrepareCursorValue('ins_test_foo', $key) . "<br>";
} }
$query = <<<EOM
INSERT INTO test_foo (
test
) VALUES (
$1
)
RETURNING test
EOM;
$db->dbPrepare("ins_test_foo_eom", $query);
$status = $db->dbExecute("ins_test_foo_eom", ['EOM BAR TEST ' . time()]);
print "EOM STRING PREPARE INSERT[ins_test_foo_eom] STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetPrepareCursorValue('ins_test_foo_eom', 'query') . " |<br>"
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "<br>";
// returning test with multiple entries // returning test with multiple entries
// $status = $db->db_exec( // $status = $db->db_exec(
// "INSERT INTO test_foo (test) VALUES " // "INSERT INTO test_foo (test) VALUES "
@@ -172,6 +213,26 @@ print "DIRECT MULTIPLE INSERT WITH RETURN STATUS: " . Support::printToString($st
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | " . "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>"; . "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
$t_1 = time();
$t_2 = time();
$t_3 = time();
$query = <<<EOM
INSERT INTO test_foo (
test
) VALUES
('EOM BAR 1 $t_1'),
('EOM BAR 2 $t_2'),
('EOM BAR 3 $t_3')
RETURNING test_foo_id, test
EOM;
$status = $db->dbExec($query);
print "EOM STRING DIRECT MULTIPLE INSERT WITH RETURN STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "PRIMARY KEYS: " . print_r($db->dbGetInsertPK(), true) . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
// no returning, but not needed ; // no returning, but not needed ;
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO; TEST " . time() . "')"); $status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO; TEST " . time() . "')");
print "DIRECT INSERT NO RETURN STATUS: " . Support::printToString($status) . " |<br>" print "DIRECT INSERT NO RETURN STATUS: " . Support::printToString($status) . " |<br>"
@@ -240,6 +301,24 @@ if ($db->dbPrepare('sel_test_foo', $q_prep) === false) {
. $db->dbGetLastError() . "/" . $db->dbGetLastWarning() . "/" . $db->dbGetLastError() . "/" . $db->dbGetLastWarning() . "/"
. "<pre>" . print_r($db->dbGetCombinedErrorHistory(), true) . "</pre><br>"; . "<pre>" . print_r($db->dbGetCombinedErrorHistory(), true) . "</pre><br>";
} }
echo "<hr>";
print "EOM STYLE STRINGS<br>";
$test_bar = $db->dbEscapeLiteral('SOMETHING DIFFERENT');
// Test EOM block
$q = <<<EOM
SELECT test_foo_id, test, some_bool, string_a, number_a,
-- comment
number_a_numeric, some_time
FROM test_foo
WHERE test = $test_bar
ORDER BY test_foo_id DESC LIMIT 5
EOM;
while (is_array($res = $db->dbReturn($q))) {
print "ROW: <pre>" . print_r($res, true) . "</pre><br>";
}
echo "<hr>";
// NOTE: try to replacate connection still exists if script is run a second time // NOTE: try to replacate connection still exists if script is run a second time
// open pg bouncer connection // open pg bouncer connection
$db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'], $log); $db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'], $log);

View File

@@ -266,16 +266,18 @@ class IO
// 1: read new, keep at end, clean before new run // 1: read new, keep at end, clean before new run
// 2: read new, clean at the end (temporary cache) // 2: read new, clean at the end (temporary cache)
// 3: never cache // 3: never cache
/** @var int */ /** @var int use cache (default) in dbReturn */
public const USE_CACHE = 0; public const USE_CACHE = 0;
/** @var int */ /** @var int reset cache and read new in dbReturn */
public const READ_NEW = 1; public const READ_NEW = 1;
/** @var int */ /** @var int clear cache after read in dbeEturn */
public const CLEAR_CACHE = 2; public const CLEAR_CACHE = 2;
/** @var int */ /** @var int do not use any cache in dbReturn */
public const NO_CACHE = 3; public const NO_CACHE = 3;
/** @var string */ /** @var string default hash type */
public const ERROR_HASH_TYPE = 'adler32'; public const ERROR_HASH_TYPE = 'adler32';
/** @var string regex to get returning with matches at position 1 */
public const REGEX_RETURNING = '/\s?returning(?: (.+?));?$/i';
// recommend to set private/protected and only allow setting via method // recommend to set private/protected and only allow setting via method
// can bet set from outside // can bet set from outside
@@ -573,14 +575,14 @@ class IO
/** /**
* checks if query is a SELECT, SHOW or WITH, if not error, 0 return * checks if query is a SELECT, SHOW or WITH, if not error, 0 return
* NOTE: * NOTE:
* Query needs to start with SELECT, SHOW or WITH. if starts with "with" it is ignored * Query needs to start with SELECT, SHOW or WITH
* @param string $query query to check * @param string $query query to check
* @return bool true if matching, false if not * @return bool true if matching, false if not
*/ */
private function __checkQueryForSelect(string $query): bool private function __checkQueryForSelect(string $query): bool
{ {
// perhaps allow spaces before select ?!? // change to string starts with?
if (preg_match("/^(select|show|with) /i", $query)) { if (preg_match("/^(?:SELECT|SHOW|WITH)\s/i", $query)) {
return true; return true;
} }
return false; return false;
@@ -597,10 +599,10 @@ class IO
*/ */
private function __checkQueryForInsert(string $query, bool $pure = false): bool private function __checkQueryForInsert(string $query, bool $pure = false): bool
{ {
if ($pure && preg_match("/^insert /i", $query)) { if ($pure && preg_match("/^INSERT\s+?INTO\s/i", $query)) {
return true; return true;
} }
if (!$pure && preg_match("/^(insert|update|delete) /i", $query)) { if (!$pure && preg_match("/^(?:INSERT\s+?INTO|DELETE\s+?FROM|UPDATE)\s/i", $query)) {
return true; return true;
} }
return false; return false;
@@ -614,7 +616,7 @@ class IO
*/ */
private function __checkQueryForUpdate(string $query): bool private function __checkQueryForUpdate(string $query): bool
{ {
if (preg_match("/^update /i", $query)) { if (preg_match("/^UPDATE\s?(.+)/i", $query)) {
return true; return true;
} }
return false; return false;
@@ -879,12 +881,32 @@ class IO
private function __dbReturnTable(string $query): array private function __dbReturnTable(string $query): array
{ {
$matches = []; $matches = [];
if (preg_match("/^SELECT /i", $query)) { $schema_table = [];
preg_match("/ (FROM) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches); if ($this->__checkQueryForSelect($query)) {
// only selects the first one, this is more a fallback
// MATCHES 1 (call), 3 (schema), 4 (table)
preg_match("/\s+?(FROM)\s+?([\"'])?(?:([\w_]+)\.)?([\w_]+)(?:\2)?\s?/i", $query, $matches);
$schema_table = [
$matches[3] ?? '',
$matches[4] ?? '',
];
} else { } else {
preg_match("/(INSERT INTO|DELETE FROM|UPDATE) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches); preg_match(
// must start with
// INSERT INTO (table)
// DELETE FROM (table)
// UPDATE (table) SET
// MATCHES 1 (call), 4 (schema), 5 (table)
"/^(INSERT\s+?INTO|DELETE\s+?FROM|(UPDATE))\s+?([\"'])?(?:([\w_]+)\.)?([\w_]+)(?:\3)?\s?(?(2)SET|)/i",
$query,
$matches
);
$schema_table = [
$matches[4] ?? '',
$matches[5] ?? ''
];
} }
return [$matches[3] ?? '', $matches[4] ?? '']; return $schema_table;
} }
/** /**
@@ -1007,7 +1029,7 @@ class IO
$this->pk_name_table[$table] : 'NULL'; $this->pk_name_table[$table] : 'NULL';
} }
if ( if (
!preg_match("/ returning /i", $this->query) && !preg_match(self::REGEX_RETURNING, $this->query) &&
$this->pk_name && $this->pk_name != 'NULL' $this->pk_name && $this->pk_name != 'NULL'
) { ) {
// check if this query has a ; at the end and remove it // check if this query has a ; at the end and remove it
@@ -1016,7 +1038,9 @@ class IO
$this->query = !is_string($__query) ? $this->query : $__query; $this->query = !is_string($__query) ? $this->query : $__query;
$this->query .= " RETURNING " . $this->pk_name; $this->query .= " RETURNING " . $this->pk_name;
$this->returning_id = true; $this->returning_id = true;
} elseif (preg_match("/ returning (.*)/i", $this->query, $matches)) { } elseif (
preg_match(self::REGEX_RETURNING, $this->query, $matches)
) {
if ($this->pk_name && $this->pk_name != 'NULL') { if ($this->pk_name && $this->pk_name != 'NULL') {
// add the primary key if it is not in the returning set // add the primary key if it is not in the returning set
if (!preg_match("/$this->pk_name/", $matches[1])) { if (!preg_match("/$this->pk_name/", $matches[1])) {
@@ -1030,7 +1054,7 @@ class IO
// if we have an UPDATE and RETURNING, flag for true, but do not add anything // if we have an UPDATE and RETURNING, flag for true, but do not add anything
if ( if (
$this->__checkQueryForUpdate($this->query) && $this->__checkQueryForUpdate($this->query) &&
preg_match("/ returning (.*)/i", $this->query, $matches) preg_match(self::REGEX_RETURNING, $this->query, $matches)
) { ) {
$this->returning_id = true; $this->returning_id = true;
} }
@@ -2288,11 +2312,14 @@ class IO
$this->prepare_cursor[$stm_name]['pk_name'] = $pk_name; $this->prepare_cursor[$stm_name]['pk_name'] = $pk_name;
} }
// if no returning, then add it // if no returning, then add it
if (!preg_match("/ returning /i", $query) && $this->prepare_cursor[$stm_name]['pk_name']) { if (
!preg_match(self::REGEX_RETURNING, $query) &&
$this->prepare_cursor[$stm_name]['pk_name']
) {
$query .= " RETURNING " . $this->prepare_cursor[$stm_name]['pk_name']; $query .= " RETURNING " . $this->prepare_cursor[$stm_name]['pk_name'];
$this->prepare_cursor[$stm_name]['returning_id'] = true; $this->prepare_cursor[$stm_name]['returning_id'] = true;
} elseif ( } elseif (
preg_match("/ returning (.*)/i", $query, $matches) && preg_match(self::REGEX_RETURNING, $query, $matches) &&
$this->prepare_cursor[$stm_name]['pk_name'] $this->prepare_cursor[$stm_name]['pk_name']
) { ) {
// if returning exists but not pk_name, add it // if returning exists but not pk_name, add it