Update DB IO classes with better RETURNING data handle

Dropped old insert_id and insert_id_ext and only kepy insert_id_arr.
Added insert_id_pk_name to get the inserted pk name.
Insert RETURNING also works on non set pk name with RETURNING set

Moved the get insert (RETURNING) into method so it can be called from
both dbExec and dbExecute flow.

Error code 31 is only thrown if no returning is set and insert flow
cannot find a primary key for this table

Update all old insert_id calls from other classes
This commit is contained in:
Clemens Schwaighofer
2021-10-27 12:11:05 +09:00
parent a6b42f243f
commit 5230cbf5d3
7 changed files with 318 additions and 265 deletions

View File

@@ -70,14 +70,14 @@ return [
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
// Change this to include the folders you wish to analyze
// (and the folders of their dependencies)
'www',
// To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your
// project depends on.
// `phan --init` will generate a list of folders for you
//'www/vendor',
// Change this to include the folders you wish to analyze
// (and the folders of their dependencies)
'www',
// To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your
// project depends on.
// `phan --init` will generate a list of folders for you
//'www/vendor',
],

View File

@@ -65,74 +65,104 @@ while (is_array($res = $db->dbReturn("SELECT * FROM max_test"))) {
// }
print "<pre>";
$status = $db->dbExec("INSERT INTO foo (test) VALUES ('FOO TEST " . time() . "') RETURNING test");
// truncate test_foo table before testing
print "<b>TRUNCATE test_foo</b><br>";
$query = "TRUNCATE test_foo";
$db->dbExec($query);
print "<b>TRUNCATE test_foobar</b><br>";
$query = "TRUNCATE test_foobar";
$db->dbExec($query);
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO TEST " . time() . "') RETURNING test");
print "DIRECT INSERT STATUS: $status | "
. "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>";
// should throw deprecated error
// $db->getReturningExt();
print "DIRECT INSERT PREVIOUS INSERTED: "
. print_r($db->dbReturnRow("SELECT foo_id, test FROM foo WHERE foo_id = " . $db->dbGetInsertPK()), true) . "<br>";
$db->dbPrepare("ins_foo", "INSERT INTO foo (test) VALUES ($1)");
$status = $db->dbExecute("ins_foo", array('BAR TEST ' . time()));
print "PREPARE INSERT STATUS: $status | "
. print_r($db->dbReturnRow("SELECT test_foo_id, test FROM test_foo "
. "WHERE test_foo_id = " . $db->dbGetInsertPK()), true) . "<br>";
// PREPARED INSERT
$db->dbPrepare("ins_test_foo", "INSERT INTO test_foo (test) VALUES ($1) RETURNING test");
$status = $db->dbExecute("ins_test_foo", array('BAR TEST ' . time()));
print "PREPARE INSERT[ins_test_foo] STATUS: $status | "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "<br>";
print "PREPARE INSERT PREVIOUS INSERTED: "
. print_r($db->dbReturnRow("SELECT foo_id, test FROM foo WHERE foo_id = " . $db->dbGetInsertPK()), true) . "<br>";
. print_r($db->dbReturnRow("SELECT test_foo_id, test FROM test_foo "
. "WHERE test_foo_id = " . $db->dbGetInsertPK()), true) . "<br>";
// returning test with multiple entries
// $status = $db->db_exec(
// "INSERT INTO foo (test) VALUES "
// "INSERT INTO test_foo (test) VALUES "
// . "('BAR 1 " . time() . "'), "
// . "('BAR 2 " . time() . "'), "
// . "('BAR 3 " . time() . "') "
// . "RETURNING foo_id"
// . "RETURNING test_foo_id"
// );
$status = $db->dbExec(
"INSERT INTO foo (test) VALUES "
"INSERT INTO test_foo (test) VALUES "
. "('BAR 1 " . time() . "'), "
. "('BAR 2 " . time() . "'), "
. "('BAR 3 " . time() . "') "
. "RETURNING foo_id, test"
. "RETURNING test_foo_id, test"
);
print "DIRECT MULTIPLE INSERT STATUS: $status | "
print "DIRECT MULTIPLE INSERT WITH RETURN STATUS: $status | "
. "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 ;
$status = $db->dbExec("INSERT INTO foo (test) VALUES ('FOO; TEST " . time() . "');");
print "DIRECT INSERT STATUS: $status | "
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO; TEST " . time() . "');");
print "DIRECT INSERT NO RETURN STATUS: $status | "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
// UPDATE WITH RETURNING
$status = $db->dbExec("UPDATE foo SET test = 'SOMETHING DIFFERENT' WHERE foo_id = 3688452 RETURNING test");
print "UPDATE STATUS: $status | "
$status = $db->dbExec("UPDATE test_foo SET test = 'SOMETHING DIFFERENT' WHERE test_foo_id = 3688452 RETURNING test");
print "UPDATE WITH RETURN STATUS: $status | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
// INSERT WITH NO RETURNING
$status = $db->dbExec("INSERT INTO test_foobar (type, integer) VALUES ('WITH DATA', 123)");
print "INSERT WITH NO PRIMARY KEY NO RETURNING STATUS: $status | "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
$status = $db->dbExec("INSERT INTO test_foobar (type, integer) VALUES ('WITH DATA', 123) RETURNING type, integer");
print "INSERT WITH NO PRIMARY KEY WITH RETURNING STATUS: $status | "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
print "</pre>";
// REEAD PREPARE
// READ PREPARE
if (
$db->dbPrepare(
'sel_foo',
"SELECT foo_id, test, some_bool, string_a, number_a, number_a_numeric, some_time "
. "FROM foo ORDER BY foo_id DESC LIMIT 5"
'sel_test_foo',
"SELECT test_foo_id, test, some_bool, string_a, number_a, number_a_numeric, some_time "
. "FROM test_foo ORDER BY test_foo_id DESC LIMIT 5"
) === false
) {
print "Error in sel_foo prepare<br>";
print "Error in sel_test_foo prepare<br>";
} else {
$max_rows = 6;
// do not run this in dbFetchArray directly as
// dbFetchArray(dbExecute(...))
// this will end in an endless loop
$cursor = $db->dbExecute('sel_foo', []);
$cursor = $db->dbExecute('sel_test_foo', []);
$i = 1;
while (($res = $db->dbFetchArray($cursor, true)) !== false) {
print "DB PREP EXEC FETCH ARR: " . $i . ": <pre>" . print_r($res, true) . "</pre><br>";
@@ -142,14 +172,16 @@ if (
# db write class test
$table = 'foo';
$table = 'test_foo';
print "TABLE META DATA: " . DgS::printAr($db->dbShowTableMetaData($table)) . "<br>";
// insert first, then use primary key to update
$primary_key = ''; # unset
$db_write_table = array('test', 'string_a', 'number_a', 'some_bool');
// $db_write_table = array('test');
$object_fields_not_touch = array();
$object_fields_not_update = array();
$data = array('test' => 'BOOL TEST SOMETHING ' . time(), 'string_a' => 'SOME TEXT', 'number_a' => 5);
$db_write_table = ['test', 'string_a', 'number_a', 'some_bool'];
$object_fields_not_touch = [];
$object_fields_not_update = [];
$data = [
'test' => 'dbWriteDataExt: BOOL TEST SOMETHING ' . time(), 'string_a' => 'SOME TEXT', 'number_a' => 5
];
$primary_key = $db->dbWriteDataExt(
$db_write_table,
$primary_key,
@@ -158,8 +190,10 @@ $primary_key = $db->dbWriteDataExt(
$object_fields_not_update,
$data
);
print "Wrote to DB tabel $table and got primary key $primary_key<br>";
$data = array('test' => 'BOOL TEST ON ' . time(), 'string_a' => '', 'number_a' => 0, 'some_bool' => 1);
print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got primary key $primary_key<br>";
$data = [
'test' => 'dbWriteDataExt: BOOL TEST ON ' . time(), 'string_a' => '', 'number_a' => 0, 'some_bool' => 1
];
$primary_key = $db->dbWriteDataExt(
$db_write_table,
$primary_key,
@@ -168,8 +202,10 @@ $primary_key = $db->dbWriteDataExt(
$object_fields_not_update,
$data
);
print "Wrote to DB tabel $table and got primary key $primary_key<br>";
$data = array('test' => 'BOOL TEST OFF ' . time(), 'string_a' => null, 'number_a' => null, 'some_bool' => 0);
print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got primary key $primary_key<br>";
$data = [
'test' => 'dbWriteDataExt: BOOL TEST OFF ' . time(), 'string_a' => null, 'number_a' => null, 'some_bool' => 0
];
$primary_key = $db->dbWriteDataExt(
$db_write_table,
$primary_key,
@@ -178,8 +214,10 @@ $primary_key = $db->dbWriteDataExt(
$object_fields_not_update,
$data
);
print "Wrote to DB tabel $table and got primary key $primary_key<br>";
$data = array('test' => 'BOOL TEST UNSET ' . time());
print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got primary key $primary_key<br>";
$data = [
'test' => 'dbWriteDataExt: BOOL TEST UNSET ' . time()
];
$primary_key = $db->dbWriteDataExt(
$db_write_table,
$primary_key,
@@ -188,7 +226,7 @@ $primary_key = $db->dbWriteDataExt(
$object_fields_not_update,
$data
);
print "Wrote to DB tabel $table and got primary key $primary_key<br>";
print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got primary key $primary_key<br>";
// return Array Test
$query = "SELECT type, sdate, integer FROM foobar";
@@ -196,7 +234,8 @@ $data = $db->dbReturnArray($query, true);
print "Full foobar list: <br><pre>" . print_r($data, true) . "</pre><br>";
# async test queries
/* $db->dbExecAsync("SELECT test FROM foo, (SELECT pg_sleep(10)) as sub WHERE foo_id IN (27, 50, 67, 44, 10)");
/*
$db->dbExecAsync("SELECT test FROM test_foo, (SELECT pg_sleep(10)) as sub WHERE test_foo_id IN (27, 50, 67, 44, 10)");
echo "WAITING FOR ASYNC: ";
$chars = array('|', '/', '-', '\\');
while (($ret = $db->dbCheckAsync()) === true)
@@ -217,7 +256,7 @@ while ($res = $db->dbFetchArray())
echo "RES: " . $res['test'] . "<br>";
}
# test async insert
$db->dbExecAsync("INSERT INTO foo (Test) VALUES ('ASYNC TEST " . time() . "')");
$db->dbExecAsync("INSERT INTO test_foo (Test) VALUES ('ASYNC TEST " . time() . "')");
echo "WAITING FOR ASYNC INSERT: ";
while (($ret = $db->dbCheckAsync()) === true)
{
@@ -227,7 +266,12 @@ while (($ret = $db->dbCheckAsync()) === true)
}
print "<br>END STATUS: " . $ret . " | PK: " . $db->insert_id . "<br>";
print "ASYNC PREVIOUS INSERTED: "
. print_r($db->dbReturnRow("SELECT foo_id, test FROM foo WHERE foo_id = " . $db->insert_id), true) . "<br>"; */
. print_r(
$db->dbReturnRow("SELECT test_foo_id, test FROM test_foo WHERE test_foo_id = "
. $db->insert_id),
true
) . "<br>";
*/
$to_db_version = '9.1.9';
print "VERSION DB: " . $db->dbVersion() . "<br>";
@@ -237,16 +281,17 @@ print "DB Version equal $to_db_version: " . $db->dbCompareVersion('=' . $to_db_v
print "DB Version bigger than $to_db_version: " . $db->dbCompareVersion('>=' . $to_db_version) . "<br>";
print "DB Version bigger $to_db_version: " . $db->dbCompareVersion('>' . $to_db_version) . "<br>";
/* $q = "SELECT FOO FRO BAR";
// $q = "Select * from foo";
$foo = $db->dbExecAsync($q);
/*
$q = "Select * from test_foo";
$test_foo = $db->dbExecAsync($q);
print "[ERR] Query: " . $q . "<br>";
print "[ERR] RESOURCE: $foo<br>";
print "[ERR] RESOURCE: $test_foo<br>";
while (($ret = $db->dbCheckAsync()) === true)
{
print "[ERR]: $ret<br>";
sleep(5);
} */
}
*/
// search path check
$q = "SHOW search_path";
@@ -260,7 +305,7 @@ $status = $db->dbExec(
. "('TIME: " . time() . "', " . rand(1, 10) . ")"
);
print "OTHER SCHEMA INSERT STATUS: "
. $status . " | PK NAME: " . $db->pk_name . ", PRIMARY KEY: " . $db->insert_id . "<br>";
. $status . " | PK NAME: " . $db->dbGetInsertPKName() . ", PRIMARY KEY: " . $db->dbGetInsertPK() . "<br>";
print "<b>NULL TEST DB READ</b><br>";
$q = "SELECT uid, null_varchar, null_int FROM test_null_data WHERE uid = 'A'";

View File

@@ -3,3 +3,5 @@
declare(strict_types=1);
require 'config.php';
// __END__

View File

@@ -539,8 +539,8 @@ class ArrayIO extends \CoreLibs\DB\IO
// set primary key
if ($insert) {
// FIXME: this has to be fixes by fixing DB::IO clas
$insert_id = $this->dbGetReturning();
if (is_bool($insert_id) || is_array($insert_id)) {
$insert_id = $this->dbGetInsertPK();
if (is_array($insert_id)) {
$insert_id = 0;
}
$this->table_array[$this->pk_name]['value'] = $insert_id;

View File

@@ -298,13 +298,10 @@ class IO extends \CoreLibs\Basic
public $num_fields; // how many fields has the query
/** @var array<mixed> */
public $field_names = []; // array with the field names of the current query
// FIXME: insert_id and insert_id_ext should not be such a mixed mess
/** @var string|int|bool|array<mixed> */
public $insert_id; // last inserted ID
/** @var string|int|bool|array<mixed> */
public $insert_id_ext; // extended insert ID (for data outside only primary key)
/** @var array<mixed> */
public $insert_id_arr; // always return as array, even if only one
private $insert_id_arr; // always return as array, even if only one
/** @var string */
private $insert_id_pk_name; // primary key name for insert recovery from insert_id_arr
/** @var string */
private $temp_sql;
// other vars
@@ -345,7 +342,7 @@ class IO extends \CoreLibs\Basic
public $pk_name_table = [];
// internal primary key name, for cross calls in async
/** @var string */
public $pk_name;
private $pk_name;
// if we use RETURNING in the INSERT call
/** @var bool */
private $returning_id = false;
@@ -604,6 +601,14 @@ class IO extends \CoreLibs\Basic
if ($type) {
$prefix .= '{<span style="font-style: italic; color: #3f0092;">' . $type . '</span>} ';
}
switch ($id) {
case 'DB_ERROR':
$prefix .= '<span style="color: レd;"><b>DB-Error</b>:</span>';
break;
case 'DB_WARNING':
$prefix .= '<span style="color: orange;"><b>DB-Warning</b>:</span>';
break;
}
if ($prefix) {
$prefix .= '- ';
}
@@ -637,9 +642,8 @@ class IO extends \CoreLibs\Basic
// write error msg ...
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $this->error_id . ': '
. $this->error_string[$this->error_id] . ($msg ? ', ' . $msg : '')
. '</span>',
$this->error_id . ': ' . $this->error_string[$this->error_id]
. ($msg ? ', ' . $msg : ''),
'DB_ERROR',
$where_called
);
@@ -649,9 +653,8 @@ class IO extends \CoreLibs\Basic
if ($this->warning_id) {
$this->__dbDebug(
'db',
'<span style="color: orange;"><b>DB-Warning</b> ' . $this->warning_id . ': '
. $this->error_string[$this->warning_id]
. ($msg ? ', ' . $msg : '') . '</span>',
$this->warning_id . ': ' . $this->error_string[$this->warning_id]
. ($msg ? ', ' . $msg : ''),
'DB_WARNING',
$where_called
);
@@ -785,7 +788,7 @@ class IO extends \CoreLibs\Basic
// check if this query has a ; at the end and remove it
$__query = preg_replace("/(;\s*)$/", '', $this->query);
// must be query, if preg replace failed, use query as before
$this->query = !is_string($__query) ? $this->query : $query;
$this->query = !is_string($__query) ? $this->query : $__query;
$this->query .= " RETURNING " . $this->pk_name;
$this->returning_id = true;
} elseif (preg_match("/ returning (.*)/i", $this->query, $matches)) {
@@ -868,83 +871,112 @@ class IO extends \CoreLibs\Basic
// count affected rows
$this->num_rows = $this->db_functions->__dbAffectedRows($this->cursor);
if (
// ONLY insert with set pk name
($this->__checkQueryForInsert($this->query, true) && $this->pk_name != 'NULL') ||
($this->__checkQueryForUpdate($this->query) && $this->returning_id)
// insert or update with returning add
($this->__checkQueryForInsert($this->query) && $this->returning_id)
) {
// set insert_id
// if we do not have a returning, we try to get it via the primary key and another select
if (!$this->returning_id) {
$this->insert_id = $this->db_functions->__dbInsertId($this->query, $this->pk_name);
$this->insert_id_ext = $this->insert_id;
$this->insert_id_arr[] = $this->insert_id;
} else {
$this->insert_id = [];
$this->insert_id_ext = [];
$this->insert_id_arr = [];
// echo "** PREPARE RETURNING FOR CURSOR: ".$this->cursor."<br>";
// we have returning, now we need to check if we get one or many returned
// we'll need to loop this, if we have multiple insert_id returns
while (
$_insert_id = $this->db_functions->__dbFetchArray(
$this->cursor,
$this->db_functions->__dbResultType(true)
)
) {
// echo "*** RETURNING: ".print_r($_insert_id, true)."<br>";
$this->insert_id[] = $_insert_id;
$this->insert_id_arr[] = $_insert_id;
}
// if we have only one, revert from array to single
if (count($this->insert_id) == 1) {
// $this->log->debug('SINGLE DATA CONVERT', count($this->insert_id[0])." => "
// .array_key_exists($this->pk_name, $this->insert_id[0]));
// $this->log->debug('PK DIRECT', (isset($this->insert_id[0][$this->pk_name]) ?
// $this->insert_id[0][$this->pk_name] : '[NO PK NAME SET]' ));
// if this has only the pk_name, then only return this,
// else array of all data (but without the position)
// example if insert_id[0]['foo'] && insert_id[0]['bar']
// it will become insert_id['foo'] & insert_id['bar']
// if only ['foo_id'] and it is the PK then the
// PK is directly written to the insert_id
if (
// FIXME: everything should be an array
/** @phpstan-ignore-next-line */
count($this->insert_id[0]) > 1 ||
/** @phpstan-ignore-next-line */
!array_key_exists($this->pk_name, $this->insert_id[0])
) {
$this->insert_id_ext = $this->insert_id[0];
/** @phpstan-ignore-next-line */
if (isset($this->insert_id[0][$this->pk_name])) {
$this->insert_id = $this->insert_id[0][$this->pk_name];
}
} elseif (isset($this->insert_id[0][$this->pk_name])) {
$this->insert_id = $this->insert_id[0][$this->pk_name];
}
} elseif (
!is_array($this->insert_id) ||
count($this->insert_id) == 0
) {
// if we have non -> error
// failed to get insert id
$this->insert_id = '';
$this->warning_id = 33;
$this->__dbError($this->cursor, '[dbExec]');
}
// if we have multiple, do not set the insert_id different, keep as array
}
// this warning handling is only for pgsql
// we returned an array of PKs instread of a single one
if (is_array($this->insert_id)) {
$this->warning_id = 32;
$this->__dbError($this->cursor, '[dbExec]');
}
$this->__dbSetInsertId(
$this->returning_id,
$this->query,
$this->pk_name,
$this->cursor
);
}
}
return true;
}
}
/**
* Get all returning variables
* try to primary get the OK into insert_id
* - one if single
* - array if many to return
* - if many this will also hold all non pk names too
* then try to fill insert_id_arr, this is always multi level
* - fill key: value as single array or multi array
* insert_id_ext
* - holds all returning as array
* TODO: Only use insert_id_arr and use functions to get ok array or single
* @param boolean $returning_id
* @param string $query
* @param string|null $pk_name
* @param resource $cursor
* @param string|null $stm_name If not null, is dbExecutre run
* @return void
*/
private function __dbSetInsertId(
bool $returning_id,
string $query,
?string $pk_name,
$cursor,
?string $stm_name = null
): void {
// $this->log->debug('DB SET INSERT ID', 'Ret: ' . ($returning_id ? 'Y' : 'N')
// . 'Q: ' . $query . ', PK: ' . $pk_name . ', S: ' . ($stm_name ?? '{-}'));
// as internval user only
$insert_id = null;
// reset internal array
$this->insert_id_arr = [];
// set the primary key name
$this->insert_id_pk_name = $pk_name ?? '';
// set insert_id
// if we do not have a returning
// we try to get it via the primary key and another select
if (!$returning_id) {
$insert_id = $this->db_functions->__dbInsertId($query, $pk_name);
$this->insert_id_arr[] = $insert_id;
// throw warning that no pk was found
if ($insert_id === false) {
$this->warning_id = 31;
$this->__dbError($cursor, '[dbExec]');
}
} elseif (
$stm_name === null ||
($stm_name !== null && !empty($cursor))
) {
// we have returning, now we need to check if we get one or many returned
// we'll need to loop this, if we have multiple insert_id returns
while (
is_array($insert_id = $this->db_functions->__dbFetchArray(
$cursor,
$this->db_functions->__dbResultType(true)
))
) {
$this->insert_id_arr[] = $insert_id;
}
// warning if we didn't get any returning data
if (count($this->insert_id_arr) == 0) {
// failed to get insert id
$this->warning_id = 33;
if ($stm_name === null) {
$this->__dbError($cursor, '[dbExec]');
} else {
$this->__dbError();
$this->__dbDebug(
'db',
$stm_name . ': RETURNING returned no data',
'DB_WARNING'
);
}
} elseif (count($this->insert_id_arr) > 1) {
// this error handling is only for INSERT (), (), ... sets
$this->warning_id = 32;
if ($stm_name === null) {
$this->__dbError($cursor, '[dbExec]');
} else {
$this->__dbError();
$this->__dbDebug(
'db',
$stm_name . ': RETURNING returned an array (possible multiple insert)',
'DB_WARNING'
);
}
}
}
}
// *************************************************************
// PUBLIC METHODS
// *************************************************************
@@ -1806,7 +1838,7 @@ class IO extends \CoreLibs\Basic
* runs a prepare query
* @param string $stm_name statement name for the query to run
* @param array<mixed> $data data to run for this query, empty array for none
* @return ?mixed false on error, or result on OK
* @return mixed false on error, or result on OK
*/
public function dbExecute(string $stm_name, array $data = [])
{
@@ -1862,89 +1894,21 @@ class IO extends \CoreLibs\Basic
return false;
}
if (
$this->__checkQueryForInsert($this->prepare_cursor[$stm_name]['query'], true) &&
$this->prepare_cursor[$stm_name]['pk_name'] != 'NULL'
// pure insert wth pk name
($this->__checkQueryForInsert($this->prepare_cursor[$stm_name]['query'], true) &&
$this->prepare_cursor[$stm_name]['pk_name'] != 'NULL') ||
// insert or update with returning set
($this->__checkQueryForInsert($this->prepare_cursor[$stm_name]['query']) &&
$this->prepare_cursor[$stm_name]['returning_id'] === true
)
) {
if (!$this->prepare_cursor[$stm_name]['returning_id']) {
$this->insert_id = $this->db_functions->__dbInsertId(
$this->prepare_cursor[$stm_name]['query'],
$this->prepare_cursor[$stm_name]['pk_name']
);
$this->insert_id_ext = $this->insert_id;
$this->insert_id_arr[] = $this->insert_id;
} elseif (!empty($result)) {
$this->insert_id = [];
$this->insert_id_ext = [];
$this->insert_id_arr = [];
// we have returning, now we need to check if we get one or many returned
// we'll need to loop this, if we have multiple insert_id returns
while (
$_insert_id = $this->db_functions->__dbFetchArray(
$result,
$this->db_functions->__dbResultType(true)
)
) {
$this->insert_id[] = $_insert_id;
$this->insert_id_arr[] = $_insert_id;
}
// if we have only one, revert from arry to single
if (count($this->insert_id) == 1) {
// $this->log->debug('SINGLE DATA CONVERT', count($this->insert_id[0])." => "
// .array_key_exists($this->prepare_cursor[$stm_name]['pk_name'], $this->insert_id[0]));
// $this->log->debug('PK DIRECT', $this->insert_id[0][$this->prepare_cursor[$stm_name]['pk_name']]);
// if this has only the pk_name, then only return this,
// else array of all data (but without the position)
// example if insert_id[0]['foo'] && insert_id[0]['bar']
// it will become insert_id['foo'] & insert_id['bar']
// if only ['foo_id'] and it is the PK then the PK is directly
// written to the insert_id
if (
// FIXME: all return insert_id should be array only
/** @phpstan-ignore-next-line */
count($this->insert_id[0]) > 1 ||
/** @phpstan-ignore-next-line */
!array_key_exists($this->prepare_cursor[$stm_name]['pk_name'], $this->insert_id[0])
) {
$this->insert_id_ext = $this->insert_id[0];
/** @phpstan-ignore-next-line */
$this->insert_id = $this->insert_id[0][$this->prepare_cursor[$stm_name]['pk_name']];
} elseif ($this->insert_id[0][$this->prepare_cursor[$stm_name]['pk_name']]) {
$this->insert_id = $this->insert_id[0][$this->prepare_cursor[$stm_name]['pk_name']];
}
} elseif (count($this->insert_id) == 0) {
// failed to get insert id
$this->insert_id = '';
$this->warning_id = 33;
$this->__dbError();
$this->__dbDebug(
'db',
'<span style="color: orange;"><b>DB-Warning</b> ' . $stm_name
. ': insert id returned no data</span>',
'DB_WARNING'
);
}
}
// this error handling is only for pgsql
if (is_array($this->insert_id)) {
$this->warning_id = 32;
$this->__dbError();
$this->__dbDebug(
'db',
'<span style="color: orange;"><b>DB-Warning</b> ' . $stm_name
. ': insert id data returned as array</span>',
'DB_WARNING'
);
} elseif (!$this->insert_id) {
// NOTE should we keep this inside
$this->warning_id = 31;
$this->__dbError();
$this->__dbDebug(
'db',
'<span style="color: orange;"><b>DB-Warning</b> ' . $stm_name
. ': Could not get insert id</span>',
'DB_WARNING'
);
}
$this->__dbSetInsertId(
$this->prepare_cursor[$stm_name]['returning_id'],
$this->prepare_cursor[$stm_name]['query'],
$this->prepare_cursor[$stm_name]['pk_name'],
$result,
$stm_name
);
}
return $result;
}
@@ -2251,7 +2215,7 @@ class IO extends \CoreLibs\Basic
return false;
}
if (!$primary_key['value']) {
$primary_key['value'] = $this->insert_id;
$primary_key['value'] = $this->dbGetInsertPK();
}
// if there is not priamry key value field return false
return $primary_key['value'] ?? false;
@@ -2334,50 +2298,91 @@ class IO extends \CoreLibs\Basic
* Array for multiple return set
* Empty string for unset
* Null for error
* @deprecated Use ->dbGetInsertPK();
*/
public function dbGetReturning()
{
// FIXME: this should be only an array
return $this->insert_id;
return $this->dbGetInsertPK();
}
/**
* alternative name, returns insert_id
* @return array<mixed>|string|int|bool|null Primary key value, most likely int
* Array for multiple return set
* Empty string for unset
* Null for error
* returns current set primary key name for last run query
* Is empty string if not setable
*
* @return string Primary key name
*/
public function dbGetInsertPKName(): string
{
return $this->insert_id_pk_name;
}
/**
* Returns current primary key for inserted row.
* Either a single element for a single insert or an array
* if multiple insert values where used.
*
* @return array<mixed>|string|int|null Current insert query primary key
*/
public function dbGetInsertPK()
{
// FIXME: this should be only an array
return $this->dbGetReturning();
return $this->dbGetReturningExt($this->insert_id_pk_name);
}
/**
* return the extended insert return string set
* Most likely Array
* @param string|null $key Optional key for insert_id_ext array
* if found will return only this element,
* else will return null
* @return array<mixed>|string|int|bool|null RETURNING values as array
* Empty string for unset
* Null for error
* Returns the full RETURNING array
* If no parameter given returns as is:
* Either as single array level for single insert
* Or nested array for multiple insert values
*
* If key was set only returns those values directly or as array
*
* On multiple insert return the position for which to return can be set too
*
* @param string|null $key
* @param integer|null $pos
* @return array<mixed>|string|int|null
*/
public function dbGetReturningExt($key = null)
public function dbGetReturningExt(?string $key = null, ?int $pos = null)
{
// FIXME: this has to be better as in return
if ($key !== null) {
if (
is_array($this->insert_id_ext) &&
isset($this->insert_id_ext[$key])
) {
return $this->insert_id_ext[$key];
// return as is if key is null
if ($key === null) {
if (count($this->insert_id_arr) == 1) {
// return as nul if not found
return $this->insert_id_arr[0] ?? null;
} else {
return null;
return $this->insert_id_arr;
}
}
return $this->insert_id_ext;
// no key string set
if (empty($key)) {
return null;
}
if (
count($this->insert_id_arr) == 1 &&
isset($this->insert_id_arr[0][$key])
) {
return $this->insert_id_arr[0][$key];
} elseif (count($this->insert_id_arr) > 1) {
// do we try to find at one position
if ($pos !== null) {
if (isset($this->insert_id_arr[$pos][$key])) {
return $this->insert_id_arr[$pos][$key];
} else {
return null;
}
} else {
// find in all inside the array
$__arr = array_column($this->insert_id_arr, $key);
if (count($__arr)) {
return $__arr;
} else {
return null;
}
}
} else {
// not found
return null;
}
}
/**
@@ -2424,6 +2429,17 @@ class IO extends \CoreLibs\Basic
return $this->num_rows ?? null;
}
/**
* Sets error number that was last
* So we always have the last error number stored even if a new one is created
*
* @return int last error number
*/
public function getHadError()
{
return $this->had_error;
}
// DEPEREACTED CALLS
/**
@@ -2446,7 +2462,7 @@ class IO extends \CoreLibs\Basic
public function getReturning()
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetReturning()', E_USER_DEPRECATED);
return $this->dbGetReturning();
return $this->dbGetInsertPK();
}
/**
@@ -2457,7 +2473,7 @@ class IO extends \CoreLibs\Basic
public function getInsertPK()
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetInsertPK()', E_USER_DEPRECATED);
return $this->dbGetReturning();
return $this->dbGetInsertPK();
}
/**
@@ -2495,16 +2511,6 @@ class IO extends \CoreLibs\Basic
return $this->dbGetNumRows();
}
/**
* Sets error number that was last
* So we always have the last error number stored even if a new one is created
*
* @return int last error number
*/
public function getHadError()
{
return $this->had_error;
}
// end if db class
}

View File

@@ -262,10 +262,10 @@ class PgSQL
* this only works if db schema is after "no plural names. and pk name is table name + _id
* detects schema prefix in table name
* @param string $query query string
* @param string $pk_name primary key name, if '' then auto detect
* @param string|null $pk_name primary key name, if '' then auto detect
* @return string|int|false primary key value
*/
public function __dbInsertId(string $query, string $pk_name)
public function __dbInsertId(string $query, ?string $pk_name)
{
// only if an insert has been done
if (preg_match("/^insert /i", $query)) {
@@ -280,7 +280,7 @@ class PgSQL
$table = $_table;
}
// no PK name given at all
if (!$pk_name) {
if (empty($pk_name)) {
// if name is plurar, make it singular
// if (preg_match("/.*s$/i", $table))
// $table = substr($table, 0, -1);

View File

@@ -1674,14 +1674,14 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
. ' (' . $this->table_array[$key]['input_name'] . ') VALUES ('
. "'" . $this->dbEscapeString($this->table_array[$key]['input_value']) . "')";
$this->dbExec($q);
if (!empty($this->table_array[$key]['where']) && is_numeric($this->insert_id)) {
if (!empty($this->table_array[$key]['where']) && is_numeric($this->dbGetInsertPK())) {
// make an update on the just inseted data with the where data als update values
$q = 'UPDATE ' . $this->table_array[$key]['table_name'] . ' SET ';
$q .= $this->table_array[$key]['where'] . ' ';
$q .= 'WHERE ' . $this->table_array[$key]['pk_name'] . ' = ' . $this->insert_id;
$q .= 'WHERE ' . $this->table_array[$key]['pk_name'] . ' = ' . $this->dbGetInsertPK();
$this->dbExec($q);
}
$this->table_array[$key]['value'] = $this->insert_id;
$this->table_array[$key]['value'] = $this->dbGetInsertPK();
} // set value from DB through select or insert
unset($this->table_array[$key]['input_value']);
} // if it is certain field type && if there is something in the temp field