From 9d79dd4af402f92f08eaec4180fe1cad739865f5 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 1 Mar 2022 20:22:59 +0900 Subject: [PATCH] Update for DB::IO DB/SQL/Pgsql: add __dbConnectionBusySocketWait for testing with loop type and socket check wait loop for async queries. (TODO) add parameter query method to supplement current normal query only function DB/IO: Make all class variables private and add needed methods to read them. Also add method for reset query in case for new calls. db error/warnings are written to proper history file with timestamp, source, etc. Update source (where called) to show the whole method stack. Move connection OK to method to easy switch to different connection open for queries check type. Never use method variable query, instead only use method parameter and error out on not set. All core calls reset errors and set error hash id for history. Bug fix for insert data return array and reset said array every time a query execution is started and not only if we have some return data to avoid having wrong (previous query) data for a new query. Add more error info to all erros where a separate db logging call was done.: ACL/Login: Do not access DB IO cursor variable directly but use method to read data. Fixes change for hash type --- www/lib/CoreLibs/ACL/Login.php | 5 +- www/lib/CoreLibs/DB/IO.php | 397 +++++++++++++++++++++--------- www/lib/CoreLibs/DB/SQL/PgSQL.php | 23 +- 3 files changed, 302 insertions(+), 123 deletions(-) diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 9363e6be..957b481a 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -439,8 +439,9 @@ class Login extends \CoreLibs\DB\IO if (!is_array($res)) { $this->login_error = 1009; $this->permission_okay = false; - } elseif (!$this->cursor_ext[md5($q)]['num_rows']) { - // username is wrong, but we throw for wrong username and wrong password the same error + } elseif (empty($this->dbGetCursorExt($q, 'num_rows'))) { + // username is wrong, but we throw for wrong username + // and wrong password the same error $this->login_error = 1010; } else { // if login errors is half of max errors and the last login error diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index c4c273bc..8f8c805c 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -72,7 +72,7 @@ * $num_rows * - the number of rows returned by a SELECT or alterd bei UPDATE/INSERT * $num_fields -* - the number of fields from the SELECT, is usefull if u do a SELECT * +* - the number of fields from the SELECT, is usefull if you do a SELECT * * $field_names * - array of field names (in the order of the return) * $insert_id @@ -253,6 +253,8 @@ declare(strict_types=1); namespace CoreLibs\DB; use CoreLibs\Create\Hash; +use CoreLibs\Debug\Support; +use CoreLibs\Create\Uids; // below no ignore is needed if we want to use PgSql interface checks with PHP 8.0 // as main system. Currently all @var sets are written as object @@ -272,14 +274,16 @@ class IO public const READ_NEW = 2; /** @var int */ public const NO_CACHE = 3; + /** @var string */ + public const ERROR_HASH_TYPE = 'adler32'; // recommend to set private/protected and only allow setting via method // can bet set from outside // encoding to /** @var string */ - public $to_encoding = ''; + private $to_encoding = ''; /** @var string */ - public $query; // the query string at the moment + private $query; // the query string at the moment // only inside // basic vars /** @var object|resource|bool|int|null */ // replace object with PgSql\Connection| @@ -307,16 +311,16 @@ class IO // FOR BELOW: (This should be private and only readable through some method) // cursor array for cached readings /** @var array */ - public $cursor_ext; // hash of hashes + private $cursor_ext; // hash of hashes // per query vars /** @var object|resource|bool */ // replace object with PgSql\Result - public $cursor; // actual cursor (DBH) + private $cursor; // actual cursor (DBH) /** @var int */ - public $num_rows; // how many rows have been found + private $num_rows; // how many rows have been found /** @var int */ - public $num_fields; // how many fields has the query + private $num_fields; // how many fields has the query /** @var array */ - public $field_names = []; // array with the field names of the current query + private $field_names = []; // array with the field names of the current query /** @var array */ private $insert_id_arr = []; // always return as array, even if only one /** @var string */ @@ -325,18 +329,14 @@ class IO /** @var string */ private $nbsp = ''; // used by print_array recursion function // error & warning id - /** @var int */ + /** @var string */ private $error_id; - /** @var int */ - // private $had_error = 0; - /** @var array */ - private $error_history = []; - /** @var int */ + /** @var string */ private $warning_id; - /** @var int */ - // private $had_warning = 0; - /** @var array */ - private $warning_history = []; + /** @var string */ + private $error_history_id; + /** @var array Stores warning and errors combinded with detail info */ + private $error_history_long = []; // error thrown on class init if we cannot connect to db /** @var bool */ protected $db_connection_closed = false; @@ -355,11 +355,11 @@ class IO protected $error_string = []; // prepared list /** @var array */ - public $prepare_cursor = []; + private $prepare_cursor = []; // primary key per table list // format is 'table' => 'pk_name' /** @var array */ - public $pk_name_table = []; + private $pk_name_table = []; // internal primary key name, for cross calls in async /** @var string */ private $pk_name; @@ -455,7 +455,7 @@ class IO // load the core DB functions wrapper class if (($db_functions = $this->__loadDBFunctions()) === null) { // abort - die(''); + die(''); } // write to internal one, once OK $this->db_functions = $db_functions; @@ -676,18 +676,43 @@ class IO $this->log->debug($debug_id, $error_string, true, $prefix); } + /** + * Reset warnings and errors before run + * Is called on base queries to reset error before each run + * Recent error history can be checked with + * dbGetErrorHistory or dbGetWarningHistory + * @return void + */ + private function __dbErrorReset(): void + { + $this->error_id = ''; + $this->warning_id = ''; + $this->error_history_id = Uids::uniqId(self::ERROR_HASH_TYPE); + } + /** * Check if there is a cursor and write this cursors error info * @param object|resource|bool $cursor current cursor for pg_result_error, * pg_last_error too, but pg_result_error * is more accurate (PgSql\Result) - * @return string If we could get the method where it was called + * @return array Pos 0: if we could get the method where it was called + * if not found [Uknown Method] + * Pos 1: if we have the pg_error_string from last error + * if nothing then empty string */ - private function __dbErrorPreprocessor($cursor = false): string + private function __dbErrorPreprocessor($cursor = false): array { $pg_error_string = ''; - // 1 = self, 2 = __dbError, __dbWarning, 3 == actual source - $where_called = \CoreLibs\Debug\Support::getCallerMethod(3); + // 1 = self/__dbErrorPreprocessor, 2 = __dbError, __dbWarning, + // 3+ == actual source + // loop until we get a null, build where called chain + // left to right for last called rightmost + $level = 3; + $where_called = null; + foreach (Support::getCallerMethodList($level) as $method) { + $where_called .= (!empty($where_called) ? '::' : '') + . $method; + } if ($where_called === null) { $where_called = '[Unknown Method]'; } @@ -700,7 +725,41 @@ class IO if ($pg_error_string) { $this->__dbDebug('db', $pg_error_string, 'DB_ERROR', $where_called); } - return $where_called; + return [$where_called, $pg_error_string]; + } + + /** + * Build combined error history + * contains timestamp, error/warning id and message + * error level, source as :: separated string + * additional pg error message if exists and optional msg given on error call + * all error messages are grouped by error_history_id set when errors are reset + * @param string $level + * @param string $error_id + * @param string $where_called + * @param string $pg_error_string + * @param string $msg + * @return void + */ + private function __dbErrorHistory( + string $level, + string $error_id, + string $where_called, + string $pg_error_string, + string $msg + ): void { + if (empty($this->error_history_id)) { + $this->error_history_id = Uids::uniqId(self::ERROR_HASH_TYPE); + } + $this->error_history_long[$this->error_history_id][] = [ + 'timestamp' => \CoreLibs\Combined\DateTime::dateStringFormat(microtime(true), true, true), + 'level' => $level, + 'id' => $error_id, + 'error' => $this->error_string[$error_id] ?? '[UNKNOWN ERROR]', + 'source' => $where_called, + 'pg_error' => $pg_error_string, + 'msg' => $msg, + ]; } /** @@ -712,18 +771,19 @@ class IO */ protected function __dbError(int $error_id, $cursor = false, string $msg = ''): void { - $where_called = $this->__dbErrorPreprocessor($cursor); + $error_id = (string)$error_id; + [$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor); // write error msg ... $this->__dbDebug( 'db', - $error_id . ': ' . $this->error_string[$error_id] + $error_id . ': ' . ($this->error_string[$error_id] ?? '[UNKNOWN ERROR]') . ($msg ? ', ' . $msg : ''), 'DB_ERROR', $where_called ); $this->error_id = $error_id; // keep warning history - $this->error_history[] = $error_id; + $this->__dbErrorHistory('error', $error_id, $where_called, $pg_error_string, $msg); } /** @@ -735,23 +795,25 @@ class IO */ protected function __dbWarning(int $warning_id, $cursor = false, string $msg = ''): void { - $where_called = $this->__dbErrorPreprocessor($cursor); + $warning_id = (string)$warning_id; + [$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor); $this->__dbDebug( 'db', - $warning_id . ': ' . $this->error_string[$warning_id] + $warning_id . ': ' . ($this->error_string[$warning_id] ?? '[UNKNOWN WARNING') . ($msg ? ', ' . $msg : ''), 'DB_WARNING', $where_called ); $this->warning_id = $warning_id; // keep warning history - $this->warning_history[] = $warning_id; + $this->__dbErrorHistory('warning', $warning_id, $where_called, $pg_error_string, $msg); } /** - * if there is the 'to_encoding' var set, and the field is in the wrong encoding converts it to the target - * @param array|bool|null $row array from fetch_row - * @return array|bool convert fetch_row array, or false + * if there is the 'to_encoding' var set, + * and the field is in the wrong encoding converts it to the target + * @param array|bool|null $row Array from fetch_row + * @return array|bool Convert fetch_row array, or false */ private function __dbConvertEncoding($row) { @@ -760,7 +822,8 @@ class IO } // only do if array, else pass through row (can be false) if ( - !is_array($row) || empty($this->to_encoding) || empty($this->db_encoding) + !is_array($row) || + empty($this->to_encoding) || empty($this->db_encoding) ) { return $row; } @@ -813,6 +876,23 @@ class IO return [$matches[3], $matches[4]]; } + /** + * check if there is another query running, or do we hang after a + * PHP error + * @param integer $timeout_seconds For complex timeout waits, default 3 seconds + * @return boolean True for connection OK, else false + */ + private function __dbCheckConnectionOk(int $timeout_seconds = 3): bool + { + // check that no other query is running right now + // FIXME: do this in a loop? __dbConnectionBusySocketWait? max check timeout + if ($this->db_functions->__dbConnectionBusy()) { + $this->__dbError(41); + return false; + } + return true; + } + /** * sub function for dbExec and dbExecAsync * - checks query is set @@ -827,15 +907,17 @@ class IO */ private function __dbPrepareExec(string $query, string $pk_name) { + // reset current cursor before exec + $this->cursor = null; + // clear matches for regex lookups $matches = []; - // to either use the returning method or the guess method for getting primary keys + // to either use the returning method + // or the guess method for getting primary keys $this->returning_id = false; // set the query - if ($query) { - $this->query = $query; - } + $this->query = $query; // no query set - if (!$this->query) { + if (empty($this->query)) { $this->__dbError(11); return false; } @@ -848,8 +930,7 @@ class IO } } // check that no other query is running right now - if ($this->db_functions->__dbConnectionBusy()) { - $this->__dbError(41); + if (!$this->__dbCheckConnectionOk()) { return false; } // if we do have an insert, check if there is no RETURNING pk_id, @@ -865,7 +946,10 @@ class IO } $this->pk_name = $this->pk_name_table[$table] ? $this->pk_name_table[$table] : 'NULL'; } - if (!preg_match("/ returning /i", $this->query) && $this->pk_name && $this->pk_name != 'NULL') { + if ( + !preg_match("/ returning /i", $this->query) && + $this->pk_name && $this->pk_name != 'NULL' + ) { // 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 @@ -911,7 +995,7 @@ class IO $this->MAX_QUERY_CALL != -1 && $this->query_called[$query_hash] > $this->MAX_QUERY_CALL ) { - $this->__dbError(30); + $this->__dbError(30, false, $this->query); $this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]'); return false; } @@ -926,6 +1010,8 @@ class IO */ private function __dbPostExec(): bool { + // always reset insert array after exec + $this->insert_id_arr = []; // if FALSE returned, set error stuff // if either the cursor is false if ($this->cursor === false || $this->db_functions->__dbLastErrorQuery()) { @@ -1009,12 +1095,7 @@ class IO if ($stm_name === null) { $this->__dbWarning(34, $cursor, '[dbExec]'); } else { - $this->__dbWarning(34); - $this->__dbDebug( - 'db', - $stm_name . ': CURSOR is null', - 'DB_WARNING' - ); + $this->__dbWarning(34, false, $stm_name . ': CURSOR is null'); } return; } @@ -1045,9 +1126,9 @@ class IO if ($stm_name === null) { $this->__dbWarning(33, $cursor, '[dbExec]'); } else { - $this->__dbWarning(33); - $this->__dbDebug( - 'db', + $this->__dbWarning( + 33, + false, $stm_name . ': RETURNING returned no data', 'DB_WARNING' ); @@ -1058,11 +1139,10 @@ class IO if ($stm_name === null) { $this->__dbWarning(32, $cursor, '[dbExec]'); } else { - $this->__dbWarning(32); - $this->__dbDebug( - 'db', - $stm_name . ': RETURNING returned an array (possible multiple insert)', - 'DB_WARNING' + $this->__dbWarning( + 32, + false, + $stm_name . ': RETURNING returned an array (possible multiple insert)' ); } } @@ -1315,7 +1395,7 @@ class IO } /** - * neutral function to escape a bytea for DB writing + * escape data for writing to bytea type column field * @param string $bytea bytea to escape * @return string escaped bytea */ @@ -1326,7 +1406,7 @@ class IO /** * clear up any data for valid DB insert - * @param int|float|string $value to escape data + * @param int|float|string|null $value to escape data * @param string $kbn escape trigger type * @return string escaped value */ @@ -1334,26 +1414,36 @@ class IO { switch ($kbn) { case 'i': - $value = $value === '' ? 'NULL' : intval($value); + $value = empty($value) ? 'NULL' : intval($value); break; case 'f': - $value = $value === '' ? 'NULL' : floatval($value); + $value = empty($value) ? 'NULL' : floatval($value); break; case 't': - $value = $value === '' ? 'NULL' : "'" . $this->dbEscapeString($value) . "'"; + $value = $value === null ? + 'NULL' : + "'" . $this->dbEscapeString($value) . "'"; break; case 'tl': - $value = $value === '' ? 'NULL' : $this->dbEscapeLiteral($value); + $value = $value === null ? + 'NULL' : + $this->dbEscapeLiteral($value); break; // what is d? case 'd': - $value = $value === '' ? 'NULL' : "'" . $this->dbEscapeString($value) . "'"; + $value = empty($value) ? 'NULL' : "'" . $this->dbEscapeString($value) . "'"; + break; + // bytea data + case 'by': + $value = empty($value) ? 'NULL' : $this->dbEscapeBytea($value); break; case 'b': - $value = $value === '' ? 'NULL' : "'" . $this->dbBoolean($value, true) . "'"; + $value = $value === '' || $value === null ? + 'NULL' : + "'" . $this->dbBoolean($value, true) . "'"; break; case 'i2': - $value = $value === '' ? 0 : intval($value); + $value = empty($value) ? 0 : intval($value); break; } return (string)$value; @@ -1475,8 +1565,8 @@ class IO */ public function dbShowTableMetaData(string $table, string $schema = '') { - $table = ($schema ? $schema . '.' : '') . $table; - + $this->__dbErrorReset(); + $table = (!empty($schema) ? $schema . '.' : '') . $table; $array = $this->db_functions->__dbMetaData($table); if (!is_array($array)) { $this->__dbError(60); @@ -1515,6 +1605,7 @@ class IO */ public function dbReturn(string $query, int $reset = self::USE_CACHE, bool $assoc_only = false) { + $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; @@ -1568,8 +1659,7 @@ class IO } } // check that no other query is running right now - if ($this->db_functions->__dbConnectionBusy()) { - $this->__dbError(41); + if (!$this->__dbCheckConnectionOk()) { return false; } $this->cursor_ext[$query_hash]['cursor'] = @@ -1734,6 +1824,7 @@ class IO */ public function dbExec(string $query = '', string $pk_name = '') { + $this->__dbErrorReset(); // prepare and check if we can actually run it if ($this->__dbPrepareExec($query, $pk_name) === false) { // bail if no query hash set @@ -1744,6 +1835,7 @@ class IO // if we faield, just set the master cursors to false too $this->cursor = $cursor; if ($cursor === false) { + $this->__dbError(13); return false; } // if FALSE returned, set error stuff @@ -1755,6 +1847,8 @@ class IO } } + // add adbExecParams(string $query = '', array $params = [], string $pk_name = ') + /** * executes a cursor and returns the data, if no more data 0 will be returned * @param object|resource|bool $cursor the cursor from db_exec or @@ -1769,7 +1863,8 @@ class IO */ public function dbFetchArray($cursor = false, bool $assoc_only = false) { - // return false if no query or cursor set ... + $this->__dbErrorReset(); + // set last available cursor if none set or false if ($cursor === false) { $cursor = $this->cursor; } @@ -1793,6 +1888,7 @@ class IO */ public function dbReturnRow(string $query, bool $assoc_only = false) { + $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; @@ -1805,7 +1901,7 @@ class IO } $cursor = $this->dbExec($query); if ($cursor === false) { - $this->__dbError(13); + return false; } $result = $this->dbFetchArray($cursor, $assoc_only); return $result; @@ -1819,6 +1915,7 @@ class IO */ public function dbReturnArray(string $query, bool $assoc_only = false) { + $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; @@ -1830,7 +1927,6 @@ class IO } $cursor = $this->dbExec($query); if ($cursor === false) { - $this->__dbError(13); return false; } $rows = []; @@ -1855,6 +1951,7 @@ class IO */ public function dbCacheReset(string $query): bool { + $this->__dbErrorReset(); $query_hash = Hash::__hashLong($query); // clears cache for this query if (!$this->cursor_ext[$query_hash]['query']) { @@ -1876,6 +1973,7 @@ class IO */ public function dbCursorPos(string $query) { + $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; @@ -1891,6 +1989,7 @@ class IO */ public function dbCursorNumRows(string $query) { + $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; @@ -1945,6 +2044,7 @@ class IO */ public function dbPrepare(string $stm_name, string $query, string $pk_name = '') { + $this->__dbErrorReset(); $matches = []; if (!$query) { $this->__dbError(11); @@ -1959,12 +2059,14 @@ class IO } } // check that no other query is running right now - if ($this->db_functions->__dbConnectionBusy()) { - $this->__dbError(41); + if (!$this->__dbCheckConnectionOk()) { return false; } // check if this was already prepared - if (!array_key_exists($stm_name, $this->prepare_cursor) || !is_array($this->prepare_cursor[$stm_name])) { + if ( + !array_key_exists($stm_name, $this->prepare_cursor) || + !is_array($this->prepare_cursor[$stm_name]) + ) { // if this is an insert query, check if we can add a return if ($this->__checkQueryForInsert($query, true)) { if ($pk_name != 'NULL') { @@ -2033,6 +2135,7 @@ class IO */ public function dbExecute(string $stm_name, array $data = []) { + $this->__dbErrorReset(); // if we do not have no prepare cursor array entry // for this statement name, abort if (!is_array($this->prepare_cursor[$stm_name])) { @@ -2115,6 +2218,7 @@ class IO */ public function dbExecAsync(string $query, string $pk_name = ''): bool { + $this->__dbErrorReset(); // prepare and check if we can actually run the query if (($query_hash = $this->__dbPrepareExec($query, $pk_name)) === false) { // bail if no hash set @@ -2141,8 +2245,10 @@ class IO */ public function dbCheckAsync() { + $this->__dbErrorReset(); // if there is actually a async query there if ($this->async_running) { + // alternative try __dbConnectionBusySocketWait if ($this->db_functions->__dbConnectionBusy()) { return true; } else { @@ -2495,6 +2601,28 @@ class IO return $client_encoding['client_encoding'] ?? ''; } + /** + * Set the to_encoding var that will trigger on return of selected data + * on the fly PHP encoding conversion + * Alternative use dbSetEcnoding to trigger encoding change on the DB side + * @param string $encoding PHP Valid encoding to set + * @return string Current set encoding + */ + public function dbSetToEncoding(string $encoding): string + { + $this->to_encoding = $encoding; + return $this->to_encoding; + } + + /** + * Returns current set to encoding + * @return string Current set encoding + */ + public function dbGetToEncoding(): string + { + return $this->to_encoding; + } + // *************************** // INTERNAL VARIABLES READ POST QUERY RUN // *************************** @@ -2609,10 +2737,13 @@ class IO * @param string|null $q Query string, if not null convert to hash * and return set cursor ext for only this * if not found or null return null - * @return array|null Cursor Extended array + * @return array|string|int|resource|object|null + * Cursor Extended array full if no parameter * Key is hash string from query run + * Or cursor data entry if query field is set + * If nothing found return null */ - public function dbGetCursorExt($q = null) + public function dbGetCursorExt($q = null, string $query_field = '') { if ($q !== null) { $query_hash = Hash::__hashLong($q); @@ -2620,7 +2751,11 @@ class IO is_array($this->cursor_ext) && isset($this->cursor_ext[$query_hash]) ) { - return $this->cursor_ext[$query_hash]; + if (empty($query_field)) { + return $this->cursor_ext[$query_hash]; + } else { + return $this->cursor_ext[$query_hash][$query_field]; + } } else { return null; } @@ -2632,13 +2767,58 @@ class IO * returns current number of rows that where * affected by UPDATE/SELECT, etc * null on empty - * @return int|null Number of rows + * @return int|null Number of rows or null if not set */ public function dbGetNumRows(): ?int { return $this->num_rows; } + /** + * Number of fields in select query + * @return integer|null Number of fields in select or null if not set + */ + public function dbGetNumFields(): ?int + { + return $this->num_fields; + } + + /** + * Return field names from query + * @return array Field names as array + */ + public function dbGetFieldNames(): array + { + return $this->field_names; + } + + /** + * Return current database handler + * @return object|resource|bool|int|null + */ + public function dbGetDbh() + { + return $this->dbh; + } + + /** + * Get current set query + * @return string Current set query string + */ + public function dbGetQuery(): string + { + return $this->query; + } + + /** + * Clear current query + * @return void + */ + public function dbResetQuery(): void + { + $this->query = ''; + } + /** * Sets error number that was last * So we always have the last error number stored even if a new @@ -2648,34 +2828,19 @@ class IO */ public function dbGetLastError(bool $transform = false): string { + // if no error, return empty + if (empty($this->error_id)) { + return ''; + } + // either error number or error detail string if (!$transform) { - return (string)$this->error_id; + return $this->error_id; } else { return $this->error_id . ': ' . ($this->error_string[$this->error_id] ?? '[NO ERROR MESSAGE]'); } } - /** - * Return the error history as is or map it to the error messages - * @param boolean $transform Set to true to transform into id + error message - * @return array Either as is, or id + error message array - */ - public function dbGetErrorHistory(bool $transform = false): array - { - if (!$transform) { - return $this->error_history; - } else { - return array_map( - function ($id) { - return $id . ': ' - . ($this->error_string[$id] ?? '[NO ERROR MESSAGE]'); - }, - $this->error_history - ); - } - } - /** * Sets warning number that was last * So we always have the last warning number stored even if a new one is created @@ -2684,8 +2849,13 @@ class IO */ public function dbGetLastWarning(bool $transform = false) { + // if no warning, return empty + if (empty($this->warning_id)) { + return ''; + } + // either warning number or warning detail string if (!$transform) { - return (string)$this->warning_id; + return $this->warning_id; } else { return $this->warning_id . ': ' . ($this->error_string[$this->warning_id] ?? '[NO WARNING MESSAGE]'); @@ -2693,23 +2863,12 @@ class IO } /** - * Return the warning history as is or map it to the error messages - * @param boolean $transform Set to true to transform into id + warning message - * @return array Either as is, or id + warning message array + * Return the combined warning and error history + * @return array */ - public function dbGetWarningHistory(bool $transform = false): array + public function dbGetCombinedErrorHistory(): array { - if (!$transform) { - return $this->warning_history; - } else { - return array_map( - function ($id) { - return $id . ': ' - . ($this->error_string[$id] ?? '[NO WARNING MESSAGE]'); - }, - $this->warning_history - ); - } + return $this->error_history_long; } /******************************** */ diff --git a/www/lib/CoreLibs/DB/SQL/PgSQL.php b/www/lib/CoreLibs/DB/SQL/PgSQL.php index 7f1efaaa..2965cc86 100644 --- a/www/lib/CoreLibs/DB/SQL/PgSQL.php +++ b/www/lib/CoreLibs/DB/SQL/PgSQL.php @@ -493,10 +493,10 @@ class PgSQL /** * wrapper for pg_meta_data * @param string $table table name - * @param bool $extended show extended info (default false) + * @param bool $extended show extended info (default true) * @return array|bool array data for the table info or false on error */ - public function __dbMetaData(string $table, $extended = false) + public function __dbMetaData(string $table, $extended = true) { if ($this->dbh === false || is_bool($this->dbh)) { return false; @@ -572,6 +572,25 @@ class PgSQL return pg_connection_busy($this->dbh); } + /** + * Experimental wrapper with scoket timetout + * @param integer $timeout_seconds Wait how many seconds on timeout + * @return boolean + */ + public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool + { + if ($this->dbh === false || is_bool($this->dbh)) { + return false; + } + $busy = pg_connection_busy($this->dbh); + $socket = [pg_socket($this->dbh)]; + while ($busy) { + // Will wait on that socket until that happens or the timeout is reached + stream_select($socket, null, null, $timeout_seconds); + $busy = pg_connection_busy($this->dbh); + } + } + /** * wrapper for pg_version * Note: this only returns server version