Compare commits

..

21 Commits

Author SHA1 Message Date
e9c791c164 Add better error reporting to DB\IO for query with params
On error with query with params the query was sent to the server and
if ther query itself is ok but there is a problem with the parameters
a wrong error message ($1 not found) will be returned

Add pg_last_error reporting to catch this too.

Update both error reporting to return not string and prefix combined
but prefix + error string in array

In error return check that both strings are not equal, so we do not
return the same error string twice.

Also default set dbh variable in the PgSQL class to false so it will
skip last error report if there is no dbh set yet.

Bug fix for db query with params debug output. if there are more than 9
entries the $1 of eg $10 is replaced with $1 entry again. Changed to
'#' instead '$' to avoid this.

Other:
ACL\Login: replace EOM with HTML
config.master: replace list() with []
Add single DB tester where we can test single db calls without adding
more to the general test run
2023-05-18 15:20:36 +09:00
c7ec1300b7 Published: v8.3.1 2023-04-26 15:43:11 +09:00
064710324e Bug fix in arraySearchKey path reset 2023-04-26 15:41:56 +09:00
e0356dcadf Release: v8.3.0 2023-04-26 14:56:11 +09:00
62a5992e3a Array combined: new arraySearchKey method
Also update publish script and move URLS to .env file
2023-04-26 14:54:13 +09:00
6bb957fcb3 Publish v8.2.2 2023-04-11 11:04:41 +09:00
0c1f060759 Merge branch 'development' 2023-04-11 11:03:24 +09:00
aad46ec80a DB\IO: add missing debug query, clean up not needed code
in dbReturn with params on not matching param the system exited on fail
without printing the query making it hard to find where the error is.
Added debug output in case the params count is not matching.
Same move in the dbExecute call

removed param count check from dbReturnRow/dbReturnArray as this check
is done in the dbExecParams call anyway
2023-04-11 11:03:04 +09:00
f5e9f0610d Publish: v8.2.1 2023-04-10 17:24:47 +09:00
14a5250cd7 DB\IO: Bug fix for missing query params replacement in debug messages 2023-04-10 17:23:27 +09:00
6e6edef57d Release: v8.2.0 2023-04-10 14:38:50 +09:00
d3810db965 Add ACL\Login additional acl fields to export acl array 2023-04-10 14:37:44 +09:00
187a012284 Published v8.1.4 2023-04-10 09:05:35 +09:00
b3d2662fd2 DB\IO params detection fix 2023-04-07 14:39:00 +09:00
1189aecae9 New release v8.1.3 2023-04-03 15:08:43 +09:00
024d6d2d7a Bug fix in DB\IO returning call check 2023-04-03 15:07:29 +09:00
f2d5377347 Release v8.1.2 2023-03-29 10:07:12 +09:00
af11bd8199 DB\IO dbReturn and dbReturnParams set NO_CACHE as default 2023-03-29 10:05:09 +09:00
0e6a43a2c2 Release v8.1.1 2023-03-28 16:49:55 +09:00
94eeaaaa51 DB\IO Debug output update for parameter queries 2023-03-28 16:49:06 +09:00
4a246bec5f Release v8.1.0 2023-03-28 16:47:38 +09:00
10 changed files with 536 additions and 111 deletions

View File

@@ -1 +1 @@
8.0.7 8.3.1

View File

@@ -20,7 +20,11 @@ fi;
# read in the .env.deploy file and we must have # read in the .env.deploy file and we must have
# GITLAB_USER # GITLAB_USER
# GITLAB_TOKEN # GITLAB_TOKEN
# GITLAB_URL
# GITEA_USER
# GITEA_DEPLOY_TOKEN # GITEA_DEPLOY_TOKEN
# GITEA_URL_DL
# GITEA_URL_PUSH
if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then
echo "Deploy enviroment file .env.deploy is missing"; echo "Deploy enviroment file .env.deploy is missing";
exit; exit;
@@ -31,30 +35,34 @@ source .env.deploy;
cd -; cd -;
set +o allexport; set +o allexport;
echo "[START]";
# gitea # gitea
if [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then if [ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] &&
[ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
curl -LJO \ curl -LJO \
--output-dir "${BASE_FOLDER}" \ --output-dir "${BASE_FOLDER}" \
https://git.egplusww.jp/Composer/CoreLibs-Composer-All/archive/v${VERSION}.zip; ${GITEA_URL_DL}/v${VERSION}.zip;
curl --user ${GITEA_USER}:${GITEA_TOKEN} \ curl --user ${GITEA_USER}:${GITEA_TOKEN} \
--upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \ --upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \
https://git.egplusww.jp/api/packages/Composer/composer?version=${VERSION}; ${GITEA_URL_PUSH}?version=${VERSION};
echo "${VERSION}" > "${file_last_published}"; echo "${VERSION}" > "${file_last_published}";
else else
echo "Missing either GITEA_USER or GITEA_TOKEN environment variable"; echo "Missing either GITEA_USER or GITEA_TOKEN environment variable";
fi; fi;
# gitlab # gitlab
if [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then if [ ! -z "${GITLAB_URL}" ] && [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
curl --data tag=v${VERSION} \ curl --data tag=v${VERSION} \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \ --header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer"; "${GITLAB_URL}";
curl --data branch=master \ curl --data branch=master \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \ --header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer"; "${GITLAB_URL}";
echo "${VERSION}" > "${file_last_published}"; echo "${VERSION}" > "${file_last_published}";
else else
echo "Missing GITLAB_DEPLOY_TOKEN environment variable"; echo "Missing GITLAB_DEPLOY_TOKEN environment variable";
fi; fi;
echo "";
echo "[DONE]";
# __END__ # __END__

View File

@@ -69,6 +69,7 @@ declare(strict_types=1);
namespace CoreLibs\ACL; namespace CoreLibs\ACL;
use CoreLibs\Check\Password; use CoreLibs\Check\Password;
use CoreLibs\Convert\Json;
class Login class Login
{ {
@@ -753,7 +754,10 @@ class Login
// we have to get the themes in here too // we have to get the themes in here too
$q = "SELECT eu.edit_user_id, eu.username, eu.password, " $q = "SELECT eu.edit_user_id, eu.username, eu.password, "
. "eu.edit_group_id, " . "eu.edit_group_id, "
. "eg.name AS edit_group_name, admin, " . "eg.name AS edit_group_name, eu.admin, "
// additinal acl lists
. "eu.additional_acl AS user_additional_acl, "
. "eg.additional_acl AS group_additional_acl, "
// login error + locked // login error + locked
. "eu.login_error_count, eu.login_error_date_last, " . "eu.login_error_count, eu.login_error_date_last, "
. "eu.login_error_date_first, eu.strict, eu.locked, " . "eu.login_error_date_first, eu.strict, eu.locked, "
@@ -901,8 +905,10 @@ class Login
$_SESSION['GROUP_NAME'] = $res['edit_group_name']; $_SESSION['GROUP_NAME'] = $res['edit_group_name'];
$_SESSION['USER_ACL_LEVEL'] = $res['user_level']; $_SESSION['USER_ACL_LEVEL'] = $res['user_level'];
$_SESSION['USER_ACL_TYPE'] = $res['user_type']; $_SESSION['USER_ACL_TYPE'] = $res['user_type'];
$_SESSION['USER_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['user_additional_acl']);
$_SESSION['GROUP_ACL_LEVEL'] = $res['group_level']; $_SESSION['GROUP_ACL_LEVEL'] = $res['group_level'];
$_SESSION['GROUP_ACL_TYPE'] = $res['group_type']; $_SESSION['GROUP_ACL_TYPE'] = $res['group_type'];
$_SESSION['GROUP_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['group_additional_acl']);
// deprecated TEMPLATE setting // deprecated TEMPLATE setting
$_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : ''; $_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : '';
$_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ? $_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ?
@@ -1021,7 +1027,8 @@ class Login
$_SESSION['PAGES'] = $pages; $_SESSION['PAGES'] = $pages;
$_SESSION['PAGES_ACL_LEVEL'] = $pages_acl; $_SESSION['PAGES_ACL_LEVEL'] = $pages_acl;
// load the edit_access user rights // load the edit_access user rights
$q = "SELECT ea.edit_access_id, level, type, ea.name, ea.color, ea.uid, edit_default " $q = "SELECT ea.edit_access_id, level, type, ea.name, "
. "ea.color, ea.uid, edit_default, ea.additional_acl "
. "FROM edit_access_user eau, edit_access_right ear, edit_access ea " . "FROM edit_access_user eau, edit_access_right ear, edit_access ea "
. "WHERE eau.edit_access_id = ea.edit_access_id " . "WHERE eau.edit_access_id = ea.edit_access_id "
. "AND eau.edit_access_right_id = ear.edit_access_right_id " . "AND eau.edit_access_right_id = ear.edit_access_right_id "
@@ -1048,6 +1055,7 @@ class Login
'uid' => $res['uid'], 'uid' => $res['uid'],
'color' => $res['color'], 'color' => $res['color'],
'default' => $res['edit_default'], 'default' => $res['edit_default'],
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
'data' => $ea_data 'data' => $ea_data
]; ];
// set the default unit // set the default unit
@@ -1122,6 +1130,11 @@ class Login
// username (login), group name // username (login), group name
$this->acl['user_name'] = $_SESSION['USER_NAME']; $this->acl['user_name'] = $_SESSION['USER_NAME'];
$this->acl['group_name'] = $_SESSION['GROUP_NAME']; $this->acl['group_name'] = $_SESSION['GROUP_NAME'];
// set additional acl
$this->acl['additional_acl'] = [
'user' => $_SESSION['USER_ADDITIONAL_ACL'],
'group' => $_SESSION['GROUP_ADDITIONAL_ACL'],
];
// we start with the default acl // we start with the default acl
$this->acl['base'] = $this->default_acl_level; $this->acl['base'] = $this->default_acl_level;
@@ -1184,7 +1197,8 @@ class Login
'uid' => $unit['uid'], 'uid' => $unit['uid'],
'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1, 'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1,
'default' => $unit['default'], 'default' => $unit['default'],
'data' => $unit['data'] 'data' => $unit['data'],
'additional_acl' => $unit['additional_acl']
]; ];
// set default // set default
if (!empty($unit['default'])) { if (!empty($unit['default'])) {
@@ -1594,7 +1608,7 @@ class Login
// TODO: submit or JS to set target page as ajax call // TODO: submit or JS to set target page as ajax call
// NOTE: for the HTML block I ignore line lengths // NOTE: for the HTML block I ignore line lengths
// phpcs:disable // phpcs:disable
$this->login_template['password_change'] = <<<EOM $this->login_template['password_change'] = <<<HTML
<div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;"> <div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;">
<table> <table>
<tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr> <tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr>
@@ -1612,7 +1626,7 @@ class Login
</table> </table>
</div> </div>
{PASSWORD_CHANGE_SHOW} {PASSWORD_CHANGE_SHOW}
EOM; HTML;
// phpcs:enable // phpcs:enable
} }
if ($this->password_forgot) { if ($this->password_forgot) {
@@ -1636,7 +1650,7 @@ EOM;
// now check templates // now check templates
// TODO: submit or JS to set target page as ajax call // TODO: submit or JS to set target page as ajax call
if (!$this->login_template['template']) { if (!$this->login_template['template']) {
$this->login_template['template'] = <<<EOM $this->login_template['template'] = <<<HTML
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{LANGUAGE}"> <html lang="{LANGUAGE}">
<head> <head>
@@ -1698,7 +1712,7 @@ h3 { font-size: 18px; }
</form> </form>
</body> </body>
</html> </html>
EOM; HTML;
} }
} }

View File

@@ -177,6 +177,65 @@ class ArrayHandler
return false; return false;
} }
/**
* search for one or many keys in array and return matching values
* If flat is set to true, return flat array with found values only
* If prefix is turned on each found group will be prefixed with the
* search key
*
* @param array<mixed> $array array to search in
* @param array<mixed> $needles keys to find in array
* @param bool $flat [false] Turn on flat output
* @param bool $prefix [false] Prefix found with needle key
* @return array<mixed> Found values
*/
public static function arraySearchKey(
array $array,
array $needles,
bool $flat = false,
bool $prefix = false
): array {
$iterator = new \RecursiveArrayIterator($array);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
);
$hit_list = [];
if ($prefix === true) {
$hit_list = array_fill_keys($needles, []);
}
$key_path = [];
$prev_depth = 0;
foreach ($recursive as $key => $value) {
if ($prev_depth > $recursive->getDepth()) {
// remove all trailing to ne depth
$diff = $prev_depth - $recursive->getDepth();
array_splice($key_path, -$diff, $diff);
}
$prev_depth = $recursive->getDepth();
if ($flat === false) {
$key_path[$recursive->getDepth()] = $key;
}
if (in_array($key, $needles, true)) {
ksort($key_path);
if ($flat === true) {
$hit = $value;
} else {
$hit = [
'value' => $value,
'path' => $key_path
];
}
if ($prefix === true) {
$hit_list[$key][] = $hit;
} else {
$hit_list[] = $hit;
}
}
}
return $hit_list;
}
/** /**
* correctly recursive merges as an array as array_merge_recursive * correctly recursive merges as an array as array_merge_recursive
* just glues things together * just glues things together

View File

@@ -279,8 +279,20 @@ class IO
public const NO_CACHE = 3; public const NO_CACHE = 3;
/** @var string default hash type */ /** @var string default hash type */
public const ERROR_HASH_TYPE = 'adler32'; public const ERROR_HASH_TYPE = 'adler32';
/**
* @var string regex for params: only stand alone $number allowed
* never allowed to start with '
* must be after space/tab, =, (
*/
public const REGEX_PARAMS = '/[^\'][\s(=](\$[0-9]{1,})/';
/** @var string regex to get returning with matches at position 1 */ /** @var string regex to get returning with matches at position 1 */
public const REGEX_RETURNING = '/\s+returning\s+(.+?);?$/i'; public const REGEX_RETURNING = '/\s+returning\s+(.+\s*(?:.+\s*)+);?$/i';
// REGEX_SELECT
// REGEX_UPDATE
// REGEX INSERT
// REGEX_INSERT_UPDATE_DELETE
// REGEX_FROM_TABLE
// REGEX_INSERT_UPDATE_DELETE_TABLE
// 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
@@ -723,7 +735,10 @@ class IO
*/ */
private function __dbErrorPreprocessor(\PgSql\Result|false $cursor = false): array private function __dbErrorPreprocessor(\PgSql\Result|false $cursor = false): array
{ {
$pg_error_string = ''; $db_prefix = '';
$db_error_string = '';
$db_prefix_last = '';
$db_error_string_last = '';
// 1 = self/__dbErrorPreprocessor, 2 = __dbError, __dbWarning, // 1 = self/__dbErrorPreprocessor, 2 = __dbError, __dbWarning,
// 3+ == actual source // 3+ == actual source
// loop until we get a null, build where called chain // loop until we get a null, build where called chain
@@ -737,16 +752,31 @@ class IO
if ($where_called === null) { if ($where_called === null) {
$where_called = '[Unknown Method]'; $where_called = '[Unknown Method]';
} }
[$db_prefix_last, $db_error_string_last] = $this->db_functions->__dbPrintLastError();
if ($cursor !== false) { if ($cursor !== false) {
$pg_error_string = $this->db_functions->__dbPrintError($cursor); [$db_prefix, $db_error_string] = $this->db_functions->__dbPrintError($cursor);
} }
if ($cursor === false && method_exists($this->db_functions, '__dbPrintError')) { if ($cursor === false && method_exists($this->db_functions, '__dbPrintError')) {
$pg_error_string = $this->db_functions->__dbPrintError(); [$db_prefix, $db_error_string] = $this->db_functions->__dbPrintError();
} }
if ($pg_error_string) { // prefix the master if not the same
$this->__dbDebug('db', $pg_error_string, 'DB_ERROR', $where_called); if (
!empty($db_error_string_last) &&
trim($db_error_string) != trim($db_error_string_last)
) {
$db_error_string =
$db_prefix_last . ' ' . $db_error_string_last . ';'
. $db_prefix . ' ' . $db_error_string;
} elseif (!empty($db_error_string)) {
$db_error_string = $db_prefix . ' ' . $db_error_string;
} }
return [$where_called, $pg_error_string]; if ($db_error_string) {
$this->__dbDebug('db', $db_error_string, 'DB_ERROR', $where_called);
}
return [
$where_called,
$db_error_string
];
} }
/** /**
@@ -890,9 +920,14 @@ class IO
// because the placeholders start with $ and at 1, // because the placeholders start with $ and at 1,
// we need to increase each key and prefix it with a $ char // we need to increase each key and prefix it with a $ char
for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) { for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) {
$keys[$i] = '$' . ($keys[$i] + 1); // note: if I use $ here, the str_replace will
// replace it again. eg $11 '$1'1would be replaced with $1 again
// prefix data set with parameter pos // prefix data set with parameter pos
$data[$i] = $keys[$i] . ':' . $data[$i]; $data[$i] = '#' . ($keys[$i] + 1) . ':' . ($data[$i] === null ?
'"NULL"' : (string)$data[$i]
);
// search part
$keys[$i] = '$' . ($keys[$i] + 1);
} }
// simply replace the $1, $2, ... with the actual data and return it // simply replace the $1, $2, ... with the actual data and return it
return str_replace( return str_replace(
@@ -1015,7 +1050,7 @@ class IO
{ {
// search for $1, $2, in the query and push it into the control array // search for $1, $2, in the query and push it into the control array
// skip counts for same eg $1, $1, $2 = 2 and not 3 // skip counts for same eg $1, $1, $2 = 2 and not 3
preg_match_all('/(\$[0-9]{1,})/', $query, $match); preg_match_all(self::REGEX_PARAMS, $query, $match);
$placeholder_count = count(array_unique($match[1])); $placeholder_count = count(array_unique($match[1]));
if ($params_count != $placeholder_count) { if ($params_count != $placeholder_count) {
$this->__dbError( $this->__dbError(
@@ -1132,7 +1167,7 @@ class IO
$this->params $this->params
), ),
'__dbPrepareExec', '__dbPrepareExec',
($this->params === [] ? 'Q' : 'Qp'), ($this->params === [] ? 'Q' : 'Qp')
); );
} }
// import protection, hash needed // import protection, hash needed
@@ -1152,7 +1187,15 @@ class IO
$this->query_called[$query_hash] > $this->MAX_QUERY_CALL $this->query_called[$query_hash] > $this->MAX_QUERY_CALL
) { ) {
$this->__dbError(30, false, $this->query); $this->__dbError(30, false, $this->query);
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]'); $this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->query,
$this->params
),
'dbExec',
($this->params === [] ? 'Q[nc]' : 'Qp[nc]')
);
return false; return false;
} }
$this->query_called[$query_hash] ++; $this->query_called[$query_hash] ++;
@@ -1819,7 +1862,7 @@ class IO
* Wrapper for dbReturnParams * Wrapper for dbReturnParams
* *
* @param string $query Query string * @param string $query Query string
* @param int $cache reset status: default: USE_CACHE * @param int $cache reset status: default: NO_CACHE
* USE_CACHE/0: normal read from cache on second run * USE_CACHE/0: normal read from cache on second run
* READ_NEW/1: write to cache, clean before new run * READ_NEW/1: write to cache, clean before new run
* CLEAR_CACHE/2: write cache, clean after finished * CLEAR_CACHE/2: write cache, clean after finished
@@ -1831,7 +1874,7 @@ class IO
*/ */
public function dbReturn( public function dbReturn(
string $query, string $query,
int $cache = self::USE_CACHE, int $cache = self::NO_CACHE,
bool $assoc_only = false bool $assoc_only = false
): array|false { ): array|false {
return $this->dbReturnParams($query, [], $cache, $assoc_only); return $this->dbReturnParams($query, [], $cache, $assoc_only);
@@ -1854,7 +1897,7 @@ class IO
* *
* @param string $query Query string * @param string $query Query string
* @param array<mixed> $params Query parameters * @param array<mixed> $params Query parameters
* @param int $cache reset status: default: USE_CACHE * @param int $cache reset status: default: NO_CACHE
* USE_CACHE/0: normal read from cache on second run * USE_CACHE/0: normal read from cache on second run
* READ_NEW/1: write to cache, clean before new run * READ_NEW/1: write to cache, clean before new run
* CLEAR_CACHE/2: write cache, clean after finished * CLEAR_CACHE/2: write cache, clean after finished
@@ -1866,7 +1909,7 @@ class IO
public function dbReturnParams( public function dbReturnParams(
string $query, string $query,
array $params = [], array $params = [],
int $cache = self::USE_CACHE, int $cache = self::NO_CACHE,
bool $assoc_only = false bool $assoc_only = false
): array|false { ): array|false {
$this->__dbErrorReset(); $this->__dbErrorReset();
@@ -1931,6 +1974,18 @@ class IO
// check if params count matches // check if params count matches
// checks if the params count given matches the expected count // checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) { if ($this->__dbCheckQueryParams($query, count($params)) === false) {
// in case we got an error print out query
if ($this->db_debug) {
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->query,
$this->params
),
'dbReturn',
($this->params === [] ? 'Q[e]' : 'Qp[e]')
);
}
return false; return false;
} }
// set first call to false // set first call to false
@@ -1954,7 +2009,15 @@ class IO
$this->cursor_ext[$query_hash]['log'][] = 'No cursor'; $this->cursor_ext[$query_hash]['log'][] = 'No cursor';
// for DEBUG, print out each query executed // for DEBUG, print out each query executed
if ($this->db_debug) { if ($this->db_debug) {
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q'); $this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->cursor_ext[$query_hash]['query'],
$this->cursor_ext[$query_hash]['params']
),
'dbReturn',
($this->cursor_ext[$query_hash]['params'] === [] ? 'Q' : 'Qp'),
);
} }
// if no DB Handler try to reconnect // if no DB Handler try to reconnect
if (!$this->dbh) { if (!$this->dbh) {
@@ -1983,7 +2046,15 @@ class IO
// if still no cursor ... // if still no cursor ...
if (!$this->cursor_ext[$query_hash]['cursor']) { if (!$this->cursor_ext[$query_hash]['cursor']) {
if ($this->db_debug) { if ($this->db_debug) {
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q'); $this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->cursor_ext[$query_hash]['query'],
$this->cursor_ext[$query_hash]['params']
),
'dbReturn',
($this->cursor_ext[$query_hash]['params'] === [] ? 'Q[e]' : 'Qp[e]'),
);
} }
// internal error handling // internal error handling
$this->__dbError(13, $this->cursor_ext[$query_hash]['cursor']); $this->__dbError(13, $this->cursor_ext[$query_hash]['cursor']);
@@ -2286,10 +2357,6 @@ class IO
$this->__dbError(17, false, $query); $this->__dbError(17, false, $query);
return false; return false;
} }
// checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) {
return false;
}
$cursor = $this->dbExecParams($query, $params); $cursor = $this->dbExecParams($query, $params);
if ($cursor === false) { if ($cursor === false) {
return false; return false;
@@ -2334,10 +2401,6 @@ class IO
$this->__dbError(17, false, $query); $this->__dbError(17, false, $query);
return false; return false;
} }
// checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) {
return false;
}
$cursor = $this->dbExecParams($query, $params); $cursor = $this->dbExecParams($query, $params);
if ($cursor === false) { if ($cursor === false) {
return false; return false;
@@ -2586,7 +2649,7 @@ class IO
$match = []; $match = [];
// search for $1, $2, in the query and push it into the control array // search for $1, $2, in the query and push it into the control array
// skip counts for same eg $1, $1, $2 = 2 and not 3 // skip counts for same eg $1, $1, $2 = 2 and not 3
preg_match_all('/(\$[0-9]{1,})/', $query, $match); preg_match_all(self::REGEX_PARAMS, $query, $match);
$this->prepare_cursor[$stm_name]['count'] = count(array_unique($match[1])); $this->prepare_cursor[$stm_name]['count'] = count(array_unique($match[1]));
$this->prepare_cursor[$stm_name]['query'] = $query; $this->prepare_cursor[$stm_name]['query'] = $query;
$result = $this->db_functions->__dbPrepare($stm_name, $query); $result = $this->db_functions->__dbPrepare($stm_name, $query);
@@ -2647,6 +2710,17 @@ class IO
); );
return false; return false;
} }
if ($this->db_debug) {
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->prepare_cursor[$stm_name]['query'],
$data
),
'dbExecPrep',
'Qpe'
);
}
// if the count does not match // if the count does not match
if ($this->prepare_cursor[$stm_name]['count'] != count($data)) { if ($this->prepare_cursor[$stm_name]['count'] != count($data)) {
$this->__dbError( $this->__dbError(
@@ -2659,17 +2733,6 @@ class IO
); );
return false; return false;
} }
if ($this->db_debug) {
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->prepare_cursor[$stm_name]['query'],
$data
),
'dbExecPrep',
'Qp'
);
}
$result = $this->db_functions->__dbExecute($stm_name, $data); $result = $this->db_functions->__dbExecute($stm_name, $data);
if ($result === false) { if ($result === false) {
$this->log->debug('ExecuteData', 'ERROR in STM[' . $stm_name . '|' $this->log->debug('ExecuteData', 'ERROR in STM[' . $stm_name . '|'

View File

@@ -209,10 +209,17 @@ interface SqlFunctions
/** /**
* Undocumented function * Undocumented function
* *
* @param \PgSql\Result|false $cursor * @return array{0:string,1:string}
* @return string
*/ */
public function __dbPrintError(\PgSql\Result|false $cursor = false): string; public function __dbPrintLastError(): array;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return array{0:string,1:string}
*/
public function __dbPrintError(\PgSql\Result|false $cursor = false): array;
/** /**
* Undocumented function * Undocumented function

View File

@@ -61,7 +61,7 @@ class PgSQL implements Interface\SqlFunctions
/** @var string */ /** @var string */
private $last_error_query; private $last_error_query;
/** @var \PgSql\Connection|false */ /** @var \PgSql\Connection|false */
private $dbh; private $dbh = false;
/** /**
* queries last error query and returns true or false if error was set * queries last error query and returns true or false if error was set
@@ -532,18 +532,37 @@ class PgSQL implements Interface\SqlFunctions
return $this->dbh; return $this->dbh;
} }
/**
* Returns last error for active cursor
*
* @return array{0:string,1:string} prefix, error string
*/
public function __dbPrintLastError(): array
{
if (is_bool($this->dbh)) {
return ['', ''];
}
if (!empty($error_message = pg_last_error($this->dbh))) {
return [
'-PostgreSQL-Error-Last-',
$error_message
];
}
return ['', ''];
}
/** /**
* reads the last error for this cursor and returns * reads the last error for this cursor and returns
* html formatted string with error name * html formatted string with error name
* *
* @param \PgSql\Result|false $cursor cursor * @param \PgSql\Result|false $cursor cursor
* or null * or null
* @return string error string * @return array{0:string,1:string} prefix, error string
*/ */
public function __dbPrintError(\PgSql\Result|false $cursor = false): string public function __dbPrintError(\PgSql\Result|false $cursor = false): array
{ {
if (is_bool($this->dbh)) { if (is_bool($this->dbh)) {
return ''; return ['', ''];
} }
// run the query again for the error result here // run the query again for the error result here
if ((is_bool($cursor)) && $this->last_error_query) { if ((is_bool($cursor)) && $this->last_error_query) {
@@ -552,10 +571,12 @@ class PgSQL implements Interface\SqlFunctions
$cursor = pg_get_result($this->dbh); $cursor = pg_get_result($this->dbh);
} }
if ($cursor && $error_str = pg_result_error($cursor)) { if ($cursor && $error_str = pg_result_error($cursor)) {
return '-PostgreSQL-Error- ' return [
. $error_str; '-PostgreSQL-Error-',
$error_str
];
} else { } else {
return ''; return ['', ''];
} }
} }

View File

@@ -267,6 +267,8 @@ final class CoreLibsACLLoginTest extends TestCase
'GROUP_ACL_LEVEL' => -1, 'GROUP_ACL_LEVEL' => -1,
'PAGES_ACL_LEVEL' => [], 'PAGES_ACL_LEVEL' => [],
'USER_ACL_LEVEL' => -1, 'USER_ACL_LEVEL' => -1,
'USER_ADDITIONAL_ACL' => [],
'GROUP_ADDITIONAL_ACL' => [],
'UNIT_UID' => [ 'UNIT_UID' => [
'AdminAccess' => 1, 'AdminAccess' => 1,
], ],
@@ -280,6 +282,7 @@ final class CoreLibsACLLoginTest extends TestCase
'data' => [ 'data' => [
'test' => 'value', 'test' => 'value',
], ],
'additional_acl' => []
], ],
], ],
// 'UNIT_DEFAULT' => '', // 'UNIT_DEFAULT' => '',

View File

@@ -31,6 +31,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
4, 4,
'b', 'b',
'c' => 'test', 'c' => 'test',
'single' => 'single',
'same' => 'same', 'same' => 'same',
'deep' => [ 'deep' => [
'sub' => [ 'sub' => [
@@ -288,6 +289,188 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @return array
*/
public function arraySearchKeyProvider(): array
{
/*
0: search in array
1: search keys
2: flat flag
3: prefix flag
4: expected array
*/
return [
// single
'find single, standard' => [
0 => self::$array,
1 => ['single'],
2 => null,
3 => null,
4 => [
0 => [
'value' => 'single',
'path' => ['single'],
],
],
],
'find single, prefix' => [
0 => self::$array,
1 => ['single'],
2 => null,
3 => true,
4 => [
'single' => [
0 => [
'value' => 'single',
'path' => ['single'],
],
],
],
],
'find single, flat' => [
0 => self::$array,
1 => ['single'],
2 => true,
3 => null,
4 => [
'single',
],
],
'find single, flat, prefix' => [
0 => self::$array,
1 => ['single'],
2 => true,
3 => true,
4 => [
'single' => [
'single',
],
],
],
// not found
'not found, standard' => [
0 => self::$array,
1 => ['NOT FOUND'],
2 => null,
3 => null,
4 => [],
],
'not found, standard, prefix' => [
0 => self::$array,
1 => ['NOT FOUND'],
2 => null,
3 => true,
4 => [
'NOT FOUND' => [],
],
],
'not found, flat' => [
0 => self::$array,
1 => ['NOT FOUND'],
2 => true,
3 => null,
4 => [],
],
'not found, flat, prefix' => [
0 => self::$array,
1 => ['NOT FOUND'],
2 => true,
3 => true,
4 => [
'NOT FOUND' => [],
],
],
// multi
'multiple found, standard' => [
0 => self::$array,
1 => ['same'],
2 => null,
3 => null,
4 => [
[
'value' => 'same',
'path' => ['a', 'same', ],
],
[
'value' => 'same',
'path' => ['same', ],
],
[
'value' => 'same',
'path' => ['deep', 'sub', 'same', ],
],
]
],
'multiple found, flat' => [
0 => self::$array,
1 => ['same'],
2 => true,
3 => null,
4 => ['same', 'same', 'same', ],
],
// search with multiple
'search multiple, standard' => [
0 => self::$array,
1 => ['single', 'nested'],
2 => null,
3 => null,
4 => [
[
'value' => 'single',
'path' => ['single'],
],
[
'value' => 'bar',
'path' => ['deep', 'sub', 'nested', ],
],
],
],
'search multiple, prefix' => [
0 => self::$array,
1 => ['single', 'nested'],
2 => null,
3 => true,
4 => [
'single' => [
[
'value' => 'single',
'path' => ['single'],
],
],
'nested' => [
[
'value' => 'bar',
'path' => ['deep', 'sub', 'nested', ],
],
],
],
],
'search multiple, flat' => [
0 => self::$array,
1 => ['single', 'nested'],
2 => true,
3 => null,
4 => [
'single', 'bar',
],
],
'search multiple, flat, prefix' => [
0 => self::$array,
1 => ['single', 'nested'],
2 => true,
3 => true,
4 => [
'single' => ['single', ],
'nested' => ['bar', ],
],
],
];
}
/** /**
* provides array listing for the merge test * provides array listing for the merge test
* *
@@ -691,6 +874,44 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
); );
} }
/**
* Undocumented function
*
* @covers::arraySearchKey
* @dataProvider arraySearchKeyProvider
* @testdox arraySearchKey Search array with keys and flat: $flat, prefix: $prefix [$_dataName]
*
* @param array $input
* @param array $needles
* @param bool|null $flat
* @param bool|null $prefix
* @param array $expected
* @return void
*/
public function testArraySearchKey(
array $input,
array $needles,
?bool $flat,
?bool $prefix,
array $expected
): void {
if ($flat === null && $prefix === null) {
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles);
} elseif ($flat === null) {
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, prefix: $prefix);
} elseif ($prefix === null) {
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, flat: $flat);
} else {
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, $flat, $prefix);
}
// print "E: " . print_r($expected, true) . "\n";
// print "R: " . print_r($result, true) . "\n";
$this->assertEquals(
$expected,
$result
);
}
/** /**
* Undocumented function * Undocumented function
* *

View File

@@ -156,7 +156,7 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExec("DROP TABLE test_meta"); $db->dbExec("DROP TABLE test_meta");
} }
// uid is for internal reference tests // uid is for internal reference tests
$base_table = <<<EOM $base_table = <<<SQL
uid VARCHAR, uid VARCHAR,
row_int INT, row_int INT,
row_numeric NUMERIC, row_numeric NUMERIC,
@@ -172,36 +172,36 @@ final class CoreLibsDBIOTest extends TestCase
row_array_varchar VARCHAR ARRAY row_array_varchar VARCHAR ARRAY
) )
WITHOUT OIDS WITHOUT OIDS
EOM; SQL;
// create the tables // create the tables
$db->dbExec( $db->dbExec(
// primary key name is table + '_id' // primary key name is table + '_id'
<<<EOM <<<SQL
CREATE TABLE table_with_primary_key ( CREATE TABLE table_with_primary_key (
table_with_primary_key_id SERIAL PRIMARY KEY, table_with_primary_key_id SERIAL PRIMARY KEY,
$base_table $base_table
EOM SQL
/* "CREATE TABLE table_with_primary_key (" /* "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(
<<<EOM <<<SQL
CREATE TABLE table_without_primary_key ( CREATE TABLE table_without_primary_key (
$base_table $base_table
EOM SQL
/* "CREATE TABLE table_without_primary_key (" /* "CREATE TABLE table_without_primary_key ("
. $base_table */ . $base_table */
); );
// create simple table for meta test // create simple table for meta test
$db->dbExec( $db->dbExec(
<<<EOM <<<SQL
CREATE TABLE test_meta ( CREATE TABLE test_meta (
row_1 VARCHAR, row_1 VARCHAR,
row_2 INT row_2 INT
) WITHOUT OIDS ) WITHOUT OIDS
EOM SQL
/* "CREATE TABLE test_meta (" /* "CREATE TABLE test_meta ("
. "row_1 VARCHAR, " . "row_1 VARCHAR, "
. "row_2 INT" . "row_2 INT"
@@ -1342,10 +1342,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
], ],
'row_2' => [ 'row_2' => [
'num' => 2, 'num' => 2,
@@ -1355,10 +1355,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
] ]
] ]
], ],
@@ -1374,10 +1374,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
], ],
'row_2' => [ 'row_2' => [
'num' => 2, 'num' => 2,
@@ -1387,10 +1387,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
] ]
] ]
], ],
@@ -2142,7 +2142,7 @@ final class CoreLibsDBIOTest extends TestCase
return [ return [
// *** READ STEP BY STEP // *** READ STEP BY STEP
// default cache: USE_CACHE // default cache: USE_CACHE
'valid select, default cache settings' => [ 'valid select, default cache settings (NO_CACHE)' => [
// 0-3 // 0-3
$read_query, $read_query,
null, null,
@@ -2156,9 +2156,7 @@ final class CoreLibsDBIOTest extends TestCase
// check cursor_ext // check cursor_ext
[ [
'cursor' => 'PgSql\Result', 'cursor' => 'PgSql\Result',
'data' => [ 'data' => [],
0 => $row_a,
],
'field_names' => [ 'field_names' => [
'row_int', 'row_int',
'uid' 'uid'
@@ -2173,9 +2171,9 @@ final class CoreLibsDBIOTest extends TestCase
'query' => $read_query, 'query' => $read_query,
'params' => [], 'params' => [],
'read_rows' => 1, 'read_rows' => 1,
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
'assoc_flag' => false, 'assoc_flag' => false,
'cached' => true, 'cached' => false,
'finished' => false, 'finished' => false,
'read_finished' => false, 'read_finished' => false,
'db_read_finished' => false, 'db_read_finished' => false,
@@ -2190,10 +2188,7 @@ final class CoreLibsDBIOTest extends TestCase
], ],
'cursor' => [ 'cursor' => [
'cursor' => 'PgSql\Result', 'cursor' => 'PgSql\Result',
'data' => [ 'data' => [],
0 => $row_a,
1 => $row_b,
],
'field_names' => [ 'field_names' => [
'row_int', 'row_int',
'uid' 'uid'
@@ -2208,9 +2203,9 @@ final class CoreLibsDBIOTest extends TestCase
'query' => $read_query, 'query' => $read_query,
'params' => [], 'params' => [],
'read_rows' => 2, 'read_rows' => 2,
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
'assoc_flag' => false, 'assoc_flag' => false,
'cached' => true, 'cached' => false,
'finished' => false, 'finished' => false,
'read_finished' => true, 'read_finished' => true,
'db_read_finished' => true, 'db_read_finished' => true,
@@ -2221,10 +2216,7 @@ final class CoreLibsDBIOTest extends TestCase
'data' => false, 'data' => false,
'cursor' => [ 'cursor' => [
'cursor' => 1, 'cursor' => 1,
'data' => [ 'data' => [],
0 => $row_a,
1 => $row_b,
],
'field_names' => [ 'field_names' => [
'row_int', 'row_int',
'uid' 'uid'
@@ -2239,9 +2231,9 @@ final class CoreLibsDBIOTest extends TestCase
'query' => $read_query, 'query' => $read_query,
'params' => [], 'params' => [],
'read_rows' => 2, 'read_rows' => 2,
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
'assoc_flag' => false, 'assoc_flag' => false,
'cached' => true, 'cached' => false,
'finished' => true, 'finished' => true,
'read_finished' => true, 'read_finished' => true,
'db_read_finished' => true, 'db_read_finished' => true,
@@ -2811,13 +2803,50 @@ final class CoreLibsDBIOTest extends TestCase
], ],
// *** READ AS LOOP // *** READ AS LOOP
// from here on a complex read all full tests // from here on a complex read all full tests
'valid select, full read DEFAULT CACHE' => [ 'valid select, full read, default cache settings (NO CACHE)' => [
$read_query, $read_query,
null, null,
null, null,
null, null,
[$row_a, $row_b,], [$row_a, $row_b,],
false, false,
[
'cursor' => 1,
'data' => [],
'field_names' => [
'row_int',
'uid'
],
'field_types' => [
'int4',
'varchar'
],
'num_fields' => 2,
'num_rows' => 2,
'pos' => 0,
'query' => $read_query,
'params' => [],
'read_rows' => 2,
'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
'assoc_flag' => false,
'cached' => false,
'finished' => true,
'read_finished' => true,
'db_read_finished' => true,
],
[],
'',
'',
$insert_query
],
// USE CACHE
'valid select, full read, USE CACHE' => [
$read_query,
null,
\CoreLibs\DB\IO::USE_CACHE,
null,
[$row_a, $row_b,],
false,
[ [
'cursor' => 1, 'cursor' => 1,
'data' => [ 'data' => [
@@ -2851,7 +2880,7 @@ final class CoreLibsDBIOTest extends TestCase
$insert_query $insert_query
], ],
// READ_NEW // READ_NEW
'valid select, full read READ NEW' => [ 'valid select, full read, READ NEW' => [
$read_query, $read_query,
null, null,
\CoreLibs\DB\IO::READ_NEW, \CoreLibs\DB\IO::READ_NEW,
@@ -2891,7 +2920,7 @@ final class CoreLibsDBIOTest extends TestCase
$insert_query $insert_query
], ],
// CLEAR_CACHE // CLEAR_CACHE
'valid select, full read CLEAR CACHE' => [ 'valid select, full read, CLEAR CACHE' => [
$read_query, $read_query,
null, null,
\CoreLibs\DB\IO::CLEAR_CACHE, \CoreLibs\DB\IO::CLEAR_CACHE,
@@ -2928,7 +2957,7 @@ final class CoreLibsDBIOTest extends TestCase
'', '',
$insert_query $insert_query
], ],
'valid select, full read NO CACHE' => [ 'valid select, full read, NO CACHE' => [
$read_query, $read_query,
null, null,
\CoreLibs\DB\IO::NO_CACHE, \CoreLibs\DB\IO::NO_CACHE,
@@ -3070,7 +3099,7 @@ final class CoreLibsDBIOTest extends TestCase
* @covers ::dbCursorPos * @covers ::dbCursorPos
* @covers ::dbCursorNumRows * @covers ::dbCursorNumRows
* @dataProvider dbReturnProvider * @dataProvider dbReturnProvider
* @testdox dbReturn Read Frist $read_first_only only and cache $flag_cache and assoc $flag_assoc with (Warning: $warning/Error: $error) [$_dataName] * @testdox dbReturn Read First $read_first_only only and cache $flag_cache and assoc $flag_assoc with (Warning: $warning/Error: $error) [$_dataName]
* *
* @param string $query * @param string $query
* @param array<mixed>|null $params * @param array<mixed>|null $params
@@ -4358,7 +4387,7 @@ final class CoreLibsDBIOTest extends TestCase
// NOTE if there are different INSERTS before the primary keys // NOTE if there are different INSERTS before the primary keys
// will not match anymore. Must be updated by hand // will not match anymore. Must be updated by hand
// IMPORTANT: if this is stand alone the primary key will not match and fail // IMPORTANT: if this is stand alone the primary key will not match and fail
$table_with_primary_key_id = 66; $table_with_primary_key_id = 68;
// 0: query + returning // 0: query + returning
// 1: params // 1: params
// 1: pk name for db exec // 1: pk name for db exec
@@ -4396,16 +4425,16 @@ final class CoreLibsDBIOTest extends TestCase
] ]
] ]
], ],
// same but as EOM // same but as heredoc
'single insert (PK), EOM string' => [ 'single insert (PK), heredoc string' => [
<<<EOM <<<SQL
INSERT INTO table_with_primary_key ( INSERT INTO table_with_primary_key (
row_varchar, row_varchar_literal, row_int, row_date row_varchar, row_varchar_literal, row_int, row_date
) VALUES ( ) VALUES (
'Text', 'Other', 123, '2022-03-01' 'Text', 'Other', 123, '2022-03-01'
) )
RETURNING row_varchar, row_varchar_literal, row_int, row_date RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM, SQL,
null, null,
null, null,
null, null,
@@ -4500,16 +4529,16 @@ final class CoreLibsDBIOTest extends TestCase
] ]
] ]
], ],
// same as above but as EOM string // same as above but as heredoc string
'single insert (No PK), EOM string' => [ 'single insert (No PK), heredoc string' => [
<<<EOM <<<SQL
INSERT INTO table_without_primary_key ( INSERT INTO table_without_primary_key (
row_varchar, row_varchar_literal, row_int, row_date row_varchar, row_varchar_literal, row_int, row_date
) VALUES ( ) VALUES (
'Text', 'Other', 123, '2022-03-01' 'Text', 'Other', 123, '2022-03-01'
) )
RETURNING row_varchar, row_varchar_literal, row_int, row_date RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM, SQL,
null, null,
null, null,
null, null,