diff --git a/.phan/config.php b/.phan/config.php index 848e87e9..68c3202a 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -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', ], diff --git a/www/admin/class_test.db.php b/www/admin/class_test.db.php index 72cc1027..3c4bc184 100644 --- a/www/admin/class_test.db.php +++ b/www/admin/class_test.db.php @@ -65,74 +65,104 @@ while (is_array($res = $db->dbReturn("SELECT * FROM max_test"))) { // } print "
";
-$status = $db->dbExec("INSERT INTO foo (test) VALUES ('FOO TEST " . time() . "') RETURNING test");
+
+// truncate test_foo table before testing
+print "TRUNCATE test_foo
"; +$query = "TRUNCATE test_foo"; +$db->dbExec($query); +print "TRUNCATE test_foobar
"; +$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) . "
"; // 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) . "
"; -$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) . "
"; + +// 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) . "
"; print "PREPARE INSERT PREVIOUS INSERTED: " - . print_r($db->dbReturnRow("SELECT foo_id, test FROM foo WHERE foo_id = " . $db->dbGetInsertPK()), true) . "
"; + . print_r($db->dbReturnRow("SELECT test_foo_id, test FROM test_foo " + . "WHERE test_foo_id = " . $db->dbGetInsertPK()), true) . "
"; // 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) . "
"; // 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) . "
"; // 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) . "
"; + + +// 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) . "
"; + +$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) . "
"; + print "
"; -// 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
"; + print "Error in sel_test_foo prepare
"; } 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 . ":
" . print_r($res, true) . "

"; @@ -142,14 +172,16 @@ if ( # db write class test -$table = 'foo'; +$table = 'test_foo'; print "TABLE META DATA: " . DgS::printAr($db->dbShowTableMetaData($table)) . "
"; +// 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
"; -$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
"; +$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
"; -$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
"; +$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
"; -$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
"; +$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
"; +print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got primary key $primary_key
"; // return Array Test $query = "SELECT type, sdate, integer FROM foobar"; @@ -196,7 +234,8 @@ $data = $db->dbReturnArray($query, true); print "Full foobar list:
" . print_r($data, true) . "

"; # 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'] . "
"; } # 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 "
END STATUS: " . $ret . " | PK: " . $db->insert_id . "
"; print "ASYNC PREVIOUS INSERTED: " - . print_r($db->dbReturnRow("SELECT foo_id, test FROM foo WHERE foo_id = " . $db->insert_id), true) . "
"; */ + . print_r( + $db->dbReturnRow("SELECT test_foo_id, test FROM test_foo WHERE test_foo_id = " + . $db->insert_id), + true + ) . "
"; +*/ $to_db_version = '9.1.9'; print "VERSION DB: " . $db->dbVersion() . "
"; @@ -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) . "
"; print "DB Version bigger $to_db_version: " . $db->dbCompareVersion('>' . $to_db_version) . "
"; -/* $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 . "
"; -print "[ERR] RESOURCE: $foo
"; +print "[ERR] RESOURCE: $test_foo
"; while (($ret = $db->dbCheckAsync()) === true) { print "[ERR]: $ret
"; 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 . "
"; + . $status . " | PK NAME: " . $db->dbGetInsertPKName() . ", PRIMARY KEY: " . $db->dbGetInsertPK() . "
"; print "NULL TEST DB READ
"; $q = "SELECT uid, null_varchar, null_int FROM test_null_data WHERE uid = 'A'"; diff --git a/www/frontend/index.php b/www/frontend/index.php index c4441331..a251ca17 100644 --- a/www/frontend/index.php +++ b/www/frontend/index.php @@ -3,3 +3,5 @@ declare(strict_types=1); require 'config.php'; + +// __END__ diff --git a/www/lib/CoreLibs/DB/Extended/ArrayIO.php b/www/lib/CoreLibs/DB/Extended/ArrayIO.php index 0dc17f63..1594a30d 100644 --- a/www/lib/CoreLibs/DB/Extended/ArrayIO.php +++ b/www/lib/CoreLibs/DB/Extended/ArrayIO.php @@ -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; diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index 875463ad..823e4c32 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -298,13 +298,10 @@ class IO extends \CoreLibs\Basic public $num_fields; // how many fields has the query /** @var array */ 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 */ - public $insert_id; // last inserted ID - /** @var string|int|bool|array */ - public $insert_id_ext; // extended insert ID (for data outside only primary key) /** @var array */ - 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 .= '{' . $type . '} '; } + switch ($id) { + case 'DB_ERROR': + $prefix .= 'DB-Error:'; + break; + case 'DB_WARNING': + $prefix .= 'DB-Warning:'; + break; + } if ($prefix) { $prefix .= '- '; } @@ -637,9 +642,8 @@ class IO extends \CoreLibs\Basic // write error msg ... $this->__dbDebug( 'db', - 'DB-Error ' . $this->error_id . ': ' - . $this->error_string[$this->error_id] . ($msg ? ', ' . $msg : '') - . '', + $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', - 'DB-Warning ' . $this->warning_id . ': ' - . $this->error_string[$this->warning_id] - . ($msg ? ', ' . $msg : '') . '', + $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."
"; - // 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)."
"; - $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 $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', - 'DB-Warning ' . $stm_name - . ': insert id returned no data', - 'DB_WARNING' - ); - } - } - // this error handling is only for pgsql - if (is_array($this->insert_id)) { - $this->warning_id = 32; - $this->__dbError(); - $this->__dbDebug( - 'db', - 'DB-Warning ' . $stm_name - . ': insert id data returned as array', - 'DB_WARNING' - ); - } elseif (!$this->insert_id) { - // NOTE should we keep this inside - $this->warning_id = 31; - $this->__dbError(); - $this->__dbDebug( - 'db', - 'DB-Warning ' . $stm_name - . ': Could not get insert id', - '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|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|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|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|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 } diff --git a/www/lib/CoreLibs/DB/SQL/PgSQL.php b/www/lib/CoreLibs/DB/SQL/PgSQL.php index b6678143..653c77c6 100644 --- a/www/lib/CoreLibs/DB/SQL/PgSQL.php +++ b/www/lib/CoreLibs/DB/SQL/PgSQL.php @@ -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); diff --git a/www/lib/CoreLibs/Output/Form/Generate.php b/www/lib/CoreLibs/Output/Form/Generate.php index b2c44729..b2afca9d 100644 --- a/www/lib/CoreLibs/Output/Form/Generate.php +++ b/www/lib/CoreLibs/Output/Form/Generate.php @@ -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