Compare commits

...

3 Commits

Author SHA1 Message Date
Clemens Schwaighofer
5a81445a28 DB\IO table match regex fix
UPDATE with SET can have spaces with variable length before
2023-01-27 11:31:26 +09:00
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
3 changed files with 126 additions and 62 deletions

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

@@ -161,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>";
@@ -186,7 +186,7 @@ EOM;
$db->dbPrepare("ins_test_foo_eom", $query); $db->dbPrepare("ins_test_foo_eom", $query);
$status = $db->dbExecute("ins_test_foo_eom", ['EOM BAR TEST ' . time()]); $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>" print "EOM STRING PREPARE INSERT[ins_test_foo_eom] STATUS: " . Support::printToString($status) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>" . "QUERY: " . $db->dbGetPrepareCursorValue('ins_test_foo_eom', '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>";

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,33 @@ 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)\s+?SET|)/i",
$query,
$matches
);
$schema_table = [
$matches[4] ?? '',
$matches[5] ?? ''
];
} }
return [$matches[3] ?? '', $matches[4] ?? '']; return $schema_table;
} }
/** /**
@@ -1007,7 +1030,7 @@ class IO
$this->pk_name_table[$table] : 'NULL'; $this->pk_name_table[$table] : 'NULL';
} }
if ( if (
!preg_match("/\s?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 +1039,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("/\s?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 +1055,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("/\s?returning (.*)/i", $this->query, $matches) preg_match(self::REGEX_RETURNING, $this->query, $matches)
) { ) {
$this->returning_id = true; $this->returning_id = true;
} }
@@ -2288,11 +2313,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("/\s?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("/\s?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