* SPDX-License-Identifier: AGPL-3.0-only ************************************/ require_once('include/logging.php'); include('adodb/adodb.inc.php'); //crmv@fix schema require_once("adodb/adodb-xmlschema03.inc.php"); //crmv@fix schema end $log = LoggerManager::getLogger('VT'); $logsqltm = LoggerManager::getLogger('SQLTIME'); // crmv@115378 require_once('include/utils/PerformancePrefs.php'); // ---- helper functions ---- //crmv@27811 function add_brakets(&$key,&$item){ $key = "[$key]"; } //crmv@27811e //crmv@24791 crmv@165479 function add_doublequotes(&$key,&$item, $force = false){ if($force || in_array($key,getOracleReservedWords())) { $key = '"'.$key.'"'; } } //crmv@24791e crmv@165479e //crmv@26687 function add_backtick(&$key,&$item){ $key = "`{$key}`"; } // Return Question mark function _questionify($v){ return "?"; } /** * Function to generate question marks for a given list of items */ function generateQuestionMarks($items_list) { // array_map will call the function specified in the first parameter for every element of the list in second parameter if (is_array($items_list)) { return implode(",", array_map("_questionify", $items_list)); } else { return implode(",", array_map("_questionify", explode(",", $items_list))); } } //crmv@26687e // Callback class useful to convert PreparedStatement Question Marks to SQL value // See function convertPS2Sql in PearDatabase below class PreparedQMark2SqlValue { // Constructor function __construct($vals){ $this->ctr = 0; $this->vals = $vals; } function call($matches){ /** * If ? is found as expected in regex used in function convert2sql * /('[^']*')|(\"[^\"]*\")|([?])/ * */ if($matches[3]=='?'){ $this->ctr++; return $this->vals[$this->ctr-1]; }else{ return $matches[0]; } } } /** * Cache Class for PearDatabase */ class PearDatabaseCache { var $_queryResultCache = Array(); var $_parent; // Cache the result if rows is less than this var $_CACHE_RESULT_ROW_LIMIT; /** * Constructor */ function __construct($parent) { $this->_parent = $parent; $this->_CACHE_RESULT_ROW_LIMIT = PerformancePrefs::getInteger('CACHE_RESULT_ROW_LIMIT', 100); } /** * Reset the cache contents */ function resetCache() { unset($this->_queryResultCache); $this->_queryResultCache = Array(); } /** * Cache SQL Query Result (perferably only SELECT SQL) */ function cacheResult($result, $sql, $params=false) { // We don't want to cache NON-SELECT query results now if(stripos(trim($sql), 'SELECT ') !== 0) { return; } // If the result is too big, don't cache it if($this->_parent->num_rows($result) > $this->_CACHE_RESULT_ROW_LIMIT) { global $log; $log->fatal("[" . get_class($this) . "] Cannot cache result! $sql [Exceeds limit ". $this->_CACHE_RESULT_ROW_LIMIT . ", Total Rows " . $this->_parent->num_rows($result) . "]"); return false; } $usekey = $sql; if(!empty($params)) $usekey = $this->_parent->convert2Sql($sql, $this->_parent->flatten_array($params)); $this->_queryResultCache[$usekey] = $result; } /** * Get the cached result for re-use */ function getCacheResult($sql, $params=false) { $result = false; $usekey = $sql; if(!empty($params)) $usekey = $this->_parent->convert2Sql($sql, $this->_parent->flatten_array($params)); $result = $this->_queryResultCache[$usekey]; // Rewind the result for re-use if($result) { // If result not in use rewind it if($result->EOF) $result->MoveFirst(); else if($result->CurrentRow() != 0) { global $log; $log->fatal("[" . get_class($this) . "] Cannot reuse result! $usekey [Rows Total " . $this->_parent->num_rows($result) . ", Currently At: " . $result->CurrentRow() . "]"); // Do no allow result to be re-used if it is in use. $result = false; } } return $result; } } class PearDatabase{ var $database = null; var $dieOnError = false; var $exceptOnError = false; // crmv@64542 var $usePersistent = true; // crmv@65455 var $dbType = null; var $dbHostName = null; var $dbName = null; var $dbOptions = null; var $userName=null; var $userPassword=null; var $dbCharset=null; // crmv@124454 var $query_time = 0; var $log = null; var $lastmysqlrow = -1; var $enableSQLlog = false; var $continueInstallOnError = true; //crmv@datadict var $datadict = null; //crmv@datadict end var $enableVTELog = true; public $deadlockRetry = 2; // crmv@62863 public $statistics = array(); // If you want to avoid executing PreparedStatement, set this to true // PreparedStatement will be converted to normal SQL statement for execution var $avoidPreparedSql = false; /** * Performance tunning parameters (can be configured through performance.prefs.php) * See the constructor for initialization */ var $isdb_default_utf8_charset = false; var $enableCache = false; var $_cacheinstance = false; // Will be auto-matically initialized if $enableCache is true var $slave = false; // crmv@185894 var $clientFlags = false; // crmv@193619 /** * API's to control cache behavior */ function __setCacheInstance($cacheInstance) { $this->_cacheinstance = $cacheInstance; } /** Return the cache instance reference (using &) */ function &getCacheInstance() { return $this->_cacheinstance; } function isCacheEnabled() { return ($this->enableCache && ($this->getCacheInstance() != false)); } function clearCache() { if($this->isCacheEnabled()) $this->getCacheInstance()->resetCache(); } function toggleCache($newstatus) { $oldstatus = $this->enableCache; $this->enableCache = $newstatus; return $oldstatus; } // END /** * Manage instance usage of this class */ static function &getInstance() { global $adb, $log; if(!isset($adb)) { $adb = new self(); } return $adb; } // END /* * Reset query result for resuing if cache is enabled. */ function resetQueryResultToEOF(&$result) { if($result) { if($result->MoveLast()) { $result->MoveNext(); } } } // END function isMySQL() { return (stripos($this->dbType ,'mysql') === 0);} //crmv@add mssql && oracle function isMssql() { return (stripos($this->dbType ,'mssql') === 0);} function isOracle() { return (stripos($this->dbType ,'oci8') === 0); } //crmv@add mssql && oracle end function isPostgres() { return $this->dbType=='pgsql'; } function println($msg) { require_once('include/logging.php'); $log1 = LoggerManager::getLogger('VT'); if(is_array($msg)) { $log1->info("PearDatabse ->".print_r($msg,true)); } else { $log1->info("PearDatabase ->".$msg); } return $msg; } function setDieOnError($value){ $this->dieOnError = $value; } function setExceptOnError($value){ $this->exceptOnError = $value; } // crmv@64542 function setDatabaseType($type){ $this->dbType = $type; } function setUserName($name){ $this->userName = $name; } function setOption($name, $value){ if(isset($this->dbOptions)) $this->dbOptions[$name] = $value; if(isset($this->database)) $this->database->setOption($name, $value); } // crmv@193619 function setSSLconnection(){ if ($this->isMySQL()) { $this->clientFlags = MYSQLI_CLIENT_SSL; } } // crmv@193619e function setUserPassword($pass){ $this->userPassword = $pass; } function setDatabaseName($db){ $this->dbName = $db; } function setDatabaseHost($host){ $this->dbHostName = $host; } function setCharset($charset){ $this->dbCharset = $charset; } // crmv@124454 function getDataSourceName(){ return $this->dbType. "://".$this->userName.":".$this->userPassword."@". $this->dbHostName . "/". $this->dbName; } function startTransaction() { if($this->isPostgres()) return; $this->checkConnection(); $this->println("TRANS Started"); $this->database->BeginTrans(); } function completeTransaction() { if($this->isPostgres()) return; if($this->database->HasFailedTrans()) $this->println("TRANS Rolled Back"); else $this->println("TRANS Commited"); $this->database->CommitTrans(); $this->println("TRANS Completed"); } function hasFailedTransaction(){ return $this->database->HasFailedTrans(); } function checkError($msg='', $dieOnError=false) { // crmv@64542 if ($this->exceptOnError) { if ($this->isMssql() && $this->hasFailedTransaction()) $this->database->RollbackTrans(); // crmv@198115 $error = "ADODB error ".$msg."->[".$this->database->ErrorNo()."]".$this->database->ErrorMsg(); throw new Exception($error); return false; } // crmv@64542e if($this->dieOnError || $dieOnError) { $bt = debug_backtrace(); $ut = array(); foreach ($bt as $t) { $ut[] = array('file'=>$t['file'],'line'=>$t['line'],'function'=>$t['function']); } echo '
';
			var_export($ut);
			echo '
'; $this->println("ADODB error ".$msg."->[".$this->database->ErrorNo()."]".$this->database->ErrorMsg()); $msg_head = trim(substr($msg,0,stripos($msg,':'))); $msg_content = trim(substr($msg,stripos($msg,':'),strlen($msg))); $query = "SELECT $replace ".substr($query, stripos($query,' FROM '),strlen($query)); echo "
$msg_head => "; echo "
$msg_content"; echo "
ADODB error => "; echo "
[".$this->database->ErrorNo()."] ".$this->database->ErrorMsg(); if ($this->isMssql() && $this->hasFailedTransaction()) $this->database->RollbackTrans(); // crmv@198115 die (); } else { $this->println("ADODB error ".$msg."->[".$this->database->ErrorNo()."]".$this->database->ErrorMsg()); } return false; } // crmv@155585 //crmv@fix space values in mssql function TrimArray($Input){ if (is_string($Input)) return trim($Input); elseif (is_array($Input)) { return array_map(array($this,__FUNCTION__), $Input); } return $Input; } //crmv@fix space values in mssql end // crmv@155585e function change_key_case($arr) { //crmv@fix space values in mssql if ($this->isMssql()){ $arr = $this->TrimArray($arr); } //crmv@fix space values in mssql end return is_array($arr)?array_change_key_case($arr):$arr; } var $req_flist; function checkConnection(){ global $log; if(!isset($this->database)) { $this->println("TRANS creating new connection"); $this->connect(false); } else { //$this->println("checkconnect using old connection"); } } /** * Put out the SQL timing information */ function logSqlTiming($startat, $endat, $sql, $params=false) { if ($this->enableVTELog) vtelog::log($sql,'SQL',$startat,$endat,$params); //crmv@47905 } /** * Execute SET NAMES UTF-8 on the connection based on configuration. */ function executeSetNamesUTF8SQL($force = false) { global $default_charset; // Performance Tuning: If database default charset is UTF-8, we don't need this if(strtoupper($default_charset) == 'UTF-8' && ($force || !$this->isdb_default_utf8_charset)) { $sql_start_time = microtime(true); $setnameSql = "SET NAMES utf8"; $this->database->Execute($setnameSql); $this->logSqlTiming($sql_start_time, microtime(true), $setnameSql); } } // crmv@63349 // Log queries to catch other temporary table creation. // You can edit the code and log any kind of query function logQuery($query, $params = null) { /* $log = false; if (preg_match('/^create temporary/i', $query)) $log = true; if ($log) { $str = "LOGGED QUERY: $query\n"; file_put_contents('/tmp/logsql.txt', $str, FILE_APPEND); } */ $showQuery = $_REQUEST['show_query'] ?? ''; $showStats = $_REQUEST['show_stats'] ?? ''; if ($showQuery == 'true' && $showStats == 'true') { $this->statistics['query_count']++; $sub6 = strtolower(substr($query, 0, 6)); if ($sub6 == 'select') { $this->statistics['select']++; } elseif ($sub6 == 'insert') { $this->statistics['insert']++; if (PHP_VERSION_ID >= 50306) { $db = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $caller = $db[1]['file'].' : '.$db[1]['line']; } $this->statistics['inserts'][] = array('query' => $query, 'params' => $params, 'caller' => $caller); } elseif ($sub6 == 'update') { $this->statistics['update']++; if (PHP_VERSION_ID >= 50306) { $db = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $caller = $db[1]['file'].' : '.$db[1]['line']; } $this->statistics['updates'][] = array('query' => $query, 'params' => $params, 'caller' => $caller); } elseif ($sub6 == 'delete') { $this->statistics['delete']++; if (PHP_VERSION_ID >= 50306) { $db = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $caller = $db[1]['file'].' : '.$db[1]['line']; } $this->statistics['deletes'][] = array('query' => $query, 'params' => $params, 'caller' => $caller); } else { $this->statistics['other']++; if (PHP_VERSION_ID >= 50306) { $db = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $caller = $db[1]['file'].' : '.$db[1]['line']; } $this->statistics['others'][] = array('query' => $query, 'params' => $params, 'caller' => $caller); } if (!is_array($this->queryHashes)) $this->queryHashes = array(); $hash = md5(strtolower($query).'_'.serialize($params)); $this->queryHashes[$hash]++; if ($this->queryHashes[$hash] > 1) { $this->statistics['duplicates_count']++; if (PHP_VERSION_ID >= 50306) { $db = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $caller = $db[1]['file'].' : '.$db[1]['line']; } $this->statistics['duplicates'][$hash] = array('query' => $query, 'params' => $params, 'count' => $this->queryHashes[$hash], 'caller' => $caller); } } } // crmv@63349e /** * Execute query in a batch. * * For example: * INSERT INTO TABLE1 VALUES (a,b); * INSERT INTO TABLE1 VALUES (c,d); * * like: INSERT INTO TABLE1 VALUES (a,b), (c,d) */ function query_batch($prefixsql, $valuearray) { if(PerformancePrefs::getBoolean('ALLOW_SQL_QUERY_BATCH')) { $sql = $prefixsql; $suffixsql = $valuearray; if(!is_array($valuearray)) $suffixsql = implode(',', $valuearray); $this->query($prefixsql . $suffixsql); } else { if(is_array($valuearray) && !empty($valuearray)) { foreach($valuearray as $suffixsql) { $this->query($prefixsql . $suffixsql); } } } } function query($sql, $dieOnError=false, $msg='', $temp=false) //crmv@70475 { global $log, $default_charset; // Performance Tuning: Have we cached the result earlier? if($this->isCacheEnabled()) { $fromcache = $this->getCacheInstance()->getCacheResult($sql); if($fromcache) { $log->debug("Using query result from cache: $sql"); return $fromcache; } } // END $log->debug('query being executed : '.$sql); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $this->logQuery($sql); // crmv@63349 $sql_start_time = microtime(true); // crmv@62863 - MySql deadlock handling $tries = 0; do { $tryAgain = false; $result = $this->database->Execute($sql); ++$tries; // try again in case of deadlock if (!$result && $tries <= $this->deadlockRetry && $this->isMySQL() && $this->database->ErrorNo() == 1213) { $tryAgain = true; } } while ($tryAgain); // crmv@62863e $this->logSqlTiming($sql_start_time, microtime(true), $sql); $this->lastmysqlrow = -1; if(!$result)$this->checkError($msg.' Query Failed:' . $sql . '::', $dieOnError); // Performance Tuning: Cache the query result if($this->isCacheEnabled()) { $this->getCacheInstance()->cacheResult($result, $sql); } // END //crmv@47905bis crmv@70475 if (!$temp && !$this->slave) { // crmv@185894 $cache_emptiers = array('create table','drop table','update table'); foreach($cache_emptiers as $ce) { if (stripos($sql,$ce) === 0) { $cache = Cache::getInstance('table_exist'); $cache->clear(); break; } } } //crmv@47905bis e crmv@70475e return $result; } /** * Convert PreparedStatement to SQL statement */ function convert2Sql($ps, $vals) { if(empty($vals)) { return $ps; } // TODO: Checks need to be added array out of bounds situations for($index = 0; $index < count($vals); $index++) { // Package import pushes data after XML parsing, so type-cast it if(is_a($vals[$index], 'SimpleXMLElement')) { $vals[$index] = (string) $vals[$index]; } if(is_string($vals[$index])) { if($vals[$index] == '') { $vals[$index] = $this->database->Quote($vals[$index]); } else { $vals[$index] = "'".$this->sql_escape_string($vals[$index]). "'"; } } elseif($vals[$index] === null) { $vals[$index] = "NULL"; } } $sql = preg_replace_callback("/('[^']*')|(\"[^\"]*\")|([?])/", array(new PreparedQMark2SqlValue($vals),"call"), $ps); return $sql; } /* ADODB prepared statement Execution * @param $sql -- Prepared sql statement * @param $params -- Parameters for the prepared statement * @param $dieOnError -- Set to true, when query execution fails * @param $msg -- Error message on query execution failure */ function pquery($sql, $params, $dieOnError=false, $msg='', $temp=false) { //crmv@70475 global $log, $default_charset; // Performance Tuning: Have we cached the result earlier? if($this->isCacheEnabled()) { $fromcache = $this->getCacheInstance()->getCacheResult($sql, $params); if($fromcache) { $log->debug("Using query result from cache: $sql"); return $fromcache; } } // END $log->debug('Prepared sql query being executed : '.$sql); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $this->logQuery($sql, $params); // crmv@63349 $sql_start_time = microtime(true); $params = $this->flatten_array($params); if (count($params) > 0) { $log->debug('Prepared sql query parameters : [' . implode(",", $params) . ']'); } // crmv@62863 - MySql deadlock handling if($this->avoidPreparedSql || empty($params)) { $sql = $this->convert2Sql($sql, $params); } $tries = 0; do { $tryAgain = false; if($this->avoidPreparedSql || empty($params)) { $result = $this->database->Execute($sql); // crmv@65455 - E_STRICT warning } else { $result = $this->database->Execute($sql, $params); // crmv@65455 } ++$tries; // try again in case of deadlock if (!$result && $tries <= $this->deadlockRetry && $this->isMySQL() && $this->database->ErrorNo() == 1213) { $tryAgain = true; } } while ($tryAgain); // crmv@62863e $sql_end_time = microtime(true); $this->logSqlTiming($sql_start_time, $sql_end_time, $sql, $params); $this->lastmysqlrow = -1; if(!$result)$this->checkError($msg.' Query Failed:' . $sql . '::', $dieOnError); // Performance Tuning: Cache the query result if($this->isCacheEnabled()) { $this->getCacheInstance()->cacheResult($result, $sql, $params); } // END //crmv@47905bis crmv@70475 if (!$temp && !$this->slave) { // crmv@185894 $cache_emptiers = array('create table','drop table','update table','rename table'); foreach($cache_emptiers as $ce) { if (stripos($sql,$ce) === 0) { $cache = Cache::getInstance('table_exist'); $cache->clear(); break; } } } //crmv@47905bis e crmv@70475e return $result; } // crmv@74560 /** * Insert many rows into the same table. This is considerably faster than * issuing multiple insert queries. */ function bulkInsert($table, $columns, $rows = array(), $chunkSize = 100, $ignore = false) { // crmv@185894 if (!is_array($rows) || count($rows) == 0) return; if (is_array($columns)) $this->format_columns($columns); if ($this->isMysql() || $this->isMsSql()) { $chunks = array_chunk($rows, $chunkSize); $ignoreSql = ($ignore ? 'IGNORE ' : ''); // crmv@185894 foreach ($chunks as $rowsInChunk) { if (is_array($columns) && count($columns) > 0) { $sql = "INSERT $ignoreSql INTO $table (".implode(',', $columns).") VALUES "; // crmv@185894 } else { $sql = "INSERT $ignoreSql INTO $table VALUES "; // crmv@185894 } $i=0; foreach ($rowsInChunk as $row) { if ($i > 0) $sql .= ", "; $sql .= "(".implode(',', array_map(array($this, 'quote'), $row)).")"; ++$i; } $this->query($sql); } } elseif ($this->isOracle()) { $chunks = array_chunk($rows, $chunkSize); foreach ($chunks as $rowsInChunk) { $sql = "INSERT ALL "; foreach ($rowsInChunk as $row) { if (is_array($columns) && count($columns) > 0) { $sql .= " INTO $table (".implode(',', $columns).") VALUES "; } else { $sql .= " INTO $table VALUES "; } $sql .= "(".implode(',', array_map(array($this, 'quote'), $row)).")"; } $sql .= " SELECT * FROM dual"; $this->query($sql); } } else { //fallback on multiple inserts if (is_array($columns) && count($columns) > 0) { $sql = "INSERT INTO $table (".implode(',', $columns).") VALUES (".generateQuestionMarks($rows[0]).")"; } else { $sql = "INSERT INTO $table VALUES (".generateQuestionMarks($rows[0]).")"; } foreach ($rows as $row) { $this->pquery($sql, $row); } } } // crmv@74560e /** * Flatten the composite array into single value. * Example: * $input = array(10, 20, array(30, 40), array('key1' => '50', 'key2'=>array(60), 70)); * returns array(10, 20, 30, 40, 50, 60, 70); */ function flatten_array($input, $output=null) { if($input == null) return array(); // crmv@167234 if($output == null) $output = array(); foreach($input as $value) { if(is_array($value)) { $output = $this->flatten_array($value, $output); } else { array_push($output, $value); } } return $output; } function getEmptyBlob($is_string=true) { //crmv@fix oracle blob if($this->isOracle()) return 'empty_blob()'; if ($is_string) return 'null'; return null; //crmv@fix oracle blob end } //crmv@fix clob function getEmptyClob($is_string=true) { if($this->isOracle()) return 'empty_clob()'; if ($is_string) return 'null'; return null; } //crmv@fix clob end function updateBlob($tablename, $colname, $id, $data) { $this->println("updateBlob t=".$tablename." c=".$colname." id=".$id); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $sql_start_time = microtime(true); $result = $this->database->UpdateBlob($tablename, $colname, $data, $id); $this->logSqlTiming($sql_start_time, microtime(true), "Update Blob $tablename, $colname, $id"); // $this->println("updateBlob t=".$tablename." c=".$colname." id=".$id." status=".$result); return $result; } function updateClob($tablename, $colname, $id, $data) { $this->println("updateBlob t=".$tablename." c=".$colname." id=".$id); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $sql_start_time = microtime(true); $result = $this->database->UpdateBlob($tablename, $colname, $data, $id,'CLOB'); $this->logSqlTiming($sql_start_time, microtime(true), "Update Clob $tablename, $colname, $id"); // $this->println("updateClob t=".$tablename." c=".$colname." id=".$id." status=".$string_result); return $result; } function updateBlobFile($tablename, $colname, $id, $filename) { $this->println("updateBlobFile t=".$tablename." c=".$colname." id=".$id." f=".$filename); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $sql_start_time = microtime(true); $result = $this->database->UpdateBlobFile($tablename, $colname, $filename, $id); $this->logSqlTiming($sql_start_time, microtime(true), "Update Blob $tablename, $colname, $id"); $this->println("updateBlobFile t=".$tablename." c=".$colname." id=".$id." f=".$filename." status=".$result); return $result; } function limitQuery($sql,$start,$count, $dieOnError=false, $msg='') { global $log; if ($start == '') $start = 0; //$this->println("ADODB limitQuery sql=".$sql." st=".$start." co=".$count); $log->debug(' limitQuery sql = '.$sql .' st = '.$start .' co = '.$count); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $sql_start_time = microtime(true); $result = $this->database->SelectLimit($sql,$count,$start); $this->logSqlTiming($sql_start_time, microtime(true),$result->sql); //crmv@47905 if(!$result) $this->checkError($msg.' Limit Query Failed:' . $sql . '::', $dieOnError); return $result; } //crmv@limit multi-database function limitpQuery($sql,$start,$count,$params=Array(),$dieOnError=false, $msg='') { global $log; if ($start == '') $start = 0; //$this->println("ADODB limitQuery sql=".$sql." st=".$start." co=".$count); $log->debug(' limitQuery sql = '.$sql .' st = '.$start .' co = '.$count); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $params = $this->flatten_array($params); if (count($params) > 0) { $log->debug('Prepared sql query parameters : [' . implode(",", $params) . ']'); } if($this->avoidPreparedSql || empty($params)) { $sql = $this->convert2Sql($sql, $params); } $sql_start_time = microtime(true); $result = $this->database->SelectLimit($sql,$count,$start); $this->logSqlTiming($sql_start_time, microtime(true),$result->sql); //crmv@47905 if(!$result) $this->checkError($msg.' Limit pQuery Failed:' . $sql . '::', $dieOnError); return $result; } //crmv@limit multi-database end function getOne($sql, $dieOnError=false, $msg='') { $this->println("ADODB getOne sql=".$sql); $this->checkConnection(); $this->executeSetNamesUTF8SQL(); $sql_start_time = microtime(true); $result = $this->database->GetOne($sql); $this->logSqlTiming($sql_start_time, microtime(true),$sql); //crmv@47905 if(!$result) $this->checkError($msg.' Get one Query Failed:' . $sql . '::', $dieOnError); return $result; } function getFieldsDefinition(&$result) { //$this->println("ADODB getFieldsArray"); $field_array = array(); if(! isset($result) || empty($result)) { return 0; } $i = 0; $n = $result->FieldCount(); while ($i < $n) { $meta = $result->FetchField($i); if (!$meta) { return 0; } array_push($field_array,$meta); $i++; } //$this->println($field_array); return $field_array; } function getFieldsArray(&$result) { //$this->println("ADODB getFieldsArray"); $field_array = array(); if(! isset($result) || empty($result)) { return 0; } $i = 0; $n = $result->FieldCount(); while ($i < $n) { $meta = $result->FetchField($i); if (!$meta) { return 0; } array_push($field_array,$meta->name); $i++; } //$this->println($field_array); return $field_array; } function getRowCount(&$result){ global $log; if(isset($result) && !empty($result)) $rows= $result->RecordCount(); return $rows; } /* ADODB newly added. replacement for mysql_num_rows */ function num_rows(&$result) { return $this->getRowCount($result); } /* ADODB newly added. replacement form mysql_num_fields */ function num_fields(&$result) { return $result->FieldCount(); } /* ADODB newly added. replacement for mysql_fetch_array() */ function fetch_array(&$result) { if($result->EOF) { //$this->println("ADODB fetch_array return null"); return NULL; } $arr = $result->FetchRow(); if(is_array($arr)) $arr = array_map('to_html', $arr); return $this->change_key_case($arr); } //crmv@get no-html data function fetch_array_no_html(&$result) { if($result->EOF) { //$this->println("ADODB fetch_array return null"); return NULL; } $arr = $result->FetchRow(); return $this->change_key_case($arr); } //crmv@get no-html data end ## adds new functions to the PearDatabase class to come around the whole ## broken query_result() idea ## Code-Contribution given by weigelt@metux.de - Starts function run_query_record_html($query) { if (!is_array($rec = $this->run_query_record($query))) return $rec; foreach ($rec as $walk => $cur) $r[$walk] = to_html($cur); return $r; } function sql_quote($data) { if (is_array($data)) { switch($data['type']) { case 'text': case 'numeric': case 'integer': case 'oid': return $this->quote($data['value']); break; case 'timestamp': return $this->formatDate($data['value']); break; default: throw new Exception("unhandled type: ".serialize($cur)); } } else return $this->quote($data); } function sql_insert_data($table, $data) { if (!$table) throw new Exception("missing table name"); if (!is_array($data)) throw new Exception("data must be an array"); if (!count($table)) throw new Exception("no data given"); $sql_fields = ''; $sql_data = ''; foreach($data as $walk => $cur) { $sql_fields .= ($sql_fields?',':'').$walk; $sql_data .= ($sql_data?',':'').$this->sql_quote($cur); } return 'INSERT INTO '.$table.' ('.$sql_fields.') VALUES ('.$sql_data.')'; } function run_insert_data($table,$data) { $query = $this->sql_insert_data($table,$data); $res = $this->query($query); $this->query("commit;"); } function run_query_record($query) { $result = $this->query($query); if (!$result) return; if (!is_object($result)) throw new Exception("query \"$query\" failed: ".serialize($result)); $res = $result->FetchRow(); $rowdata = $this->change_key_case($res); return $rowdata; } function run_query_allrecords($query) { $result = $this->query($query); $records = array(); $sz = $this->num_rows($result); for ($i=0; $i<$sz; $i++) $records[$i] = $this->change_key_case($result->FetchRow()); return $records; } function run_query_field($query,$field='') { $rowdata = $this->run_query_record($query); if(isset($field) && $field != '') return $rowdata[$field]; else return array_shift($rowdata); } function run_query_list($query,$field){ $records = $this->run_query_allrecords($query); foreach($records as $walk => $cur) $list[] = $cur[$field]; } function run_query_field_html($query,$field){ return to_html($this->run_query_field($query,$field)); } function result_get_next_record($result){ return $this->change_key_case($result->FetchRow()); } // create an IN expression from an array/list function sql_expr_datalist($a) { if (!is_array($a)) throw new Exception("not an array"); if (!count($a)) throw new Exception("empty arrays not allowed"); foreach($a as $walk => $cur) $l .= ($l?',':'').$this->quote($cur); return ' ( '.$l.' ) '; } // create an IN expression from an record list, take $field within each record function sql_expr_datalist_from_records($a,$field) { if (!is_array($a)) throw new Exception("not an array"); if (!$field) throw new Exception("missing field"); if (!count($a)) throw new Exception("empty arrays not allowed"); foreach($a as $walk => $cur) $l .= ($l?',':'').$this->quote($cur[$field]); return ' ( '.$l.' ) '; } function sql_concat($list) { //crmv@fix concat multi-database return call_user_func_array(array($this->database,'Concat'),$list); //crmv@fix concat multi-database end } ## Code-Contribution given by weigelt@metux.de - Ends /* ADODB newly added. replacement for mysql_result() */ function query_result(&$result, $row, $col=0) { if (!is_object($result)) throw new Exception("result is not an object"); $result->Move($row); $rowdata = $this->change_key_case($result->FetchRow()); //$this->println($rowdata); if($col == 'fieldlabel') $coldata = $rowdata[$col]; else $coldata = to_html($rowdata[$col]); return $coldata; } //crmv@fix no-html data function query_result_no_html(&$result, $row, $col=0) { if (!is_object($result)) throw new Exception("result is not an object"); $result->Move($row); $rowdata = $this->change_key_case($result->FetchRow()); //$this->println($rowdata); $coldata = $rowdata[$col]; return $coldata; } //crmv@fix no-html data end // Function to get particular row from the query result function query_result_rowdata(&$result, $row=0) { if (!is_object($result)) throw new Exception("result is not an object"); $result->Move($row); $rowdata = $this->change_key_case($result->FetchRow()); if ($rowdata) { foreach($rowdata as $col => $coldata) { if($col != 'fieldlabel') $rowdata[$col] = to_html($coldata); } } return $rowdata; } /** * Get an array representing a row in the result set * Unlike it's non raw siblings this method will not escape * html entities in return strings. * * The case of all the field names is converted to lower case. * as with the other methods. * * @param &$result The query result to fetch from. * @param $row The row number to fetch. It's default value is 0 * */ function raw_query_result_rowdata(&$result, $row=0) { if (!is_object($result)) throw new Exception("result is not an object"); $result->Move($row); $rowdata = $this->change_key_case($result->FetchRow()); return $rowdata; } function getAffectedRowCount(&$result){ global $log; $log->debug('getAffectedRowCount'); $rows =$this->database->Affected_Rows(); $log->debug('getAffectedRowCount rows = '.$rows); return $rows; } function requireSingleResult($sql, $dieOnError=false,$msg='', $encode=true) { $result = $this->query($sql, $dieOnError, $msg); if($this->getRowCount($result ) == 1) return $result; $this->log->error('Rows Returned:'. $this->getRowCount($result) .' More than 1 row returned for '. $sql); return ''; } /* function which extends requireSingleResult api to execute prepared statment */ function requirePsSingleResult($sql, $params, $dieOnError=false,$msg='', $encode=true) { $result = $this->pquery($sql, $params, $dieOnError, $msg); if($this->getRowCount($result ) == 1) return $result; $this->log->error('Rows Returned:'. $this->getRowCount($result) .' More than 1 row returned for '. $sql); return ''; } function fetchByAssoc(&$result, $rowNum = -1, $encode=true) { if($result->EOF) { $this->println("ADODB fetchByAssoc return null"); return NULL; } if(isset($result) && $rowNum < 0) { $row = $this->change_key_case($result->GetRowAssoc(false)); $result->MoveNext(); if($encode&& is_array($row)) return array_map('to_html', $row); return $row; } if($this->getRowCount($result) > $rowNum) { $result->Move($rowNum); } $this->lastmysqlrow = $rowNum; $row = $this->change_key_case($result->GetRowAssoc(false)); $result->MoveNext(); $this->println($row); if($encode&& is_array($row)) return array_map('to_html', $row); return $row; } function getNextRow(&$result, $encode=true){ global $log; $log->info('getNextRow'); if(isset($result)){ $row = $this->change_key_case($result->FetchRow()); if($row && $encode&& is_array($row)) return array_map('to_html', $row); return $row; } return null; } function fetch_row(&$result, $encode=true) { return $this->getNextRow($result); } function field_name(&$result, $col) { return $result->FetchField($col); } function getQueryTime(){ return $this->query_time; } function connect($dieOnError = false) { global $dbconfigoption,$dbconfig; if(!isset($this->dbType)) { $this->println("ADODB Connect : DBType not specified"); return; } $this->database = ADONewConnection($this->dbType); //crmv@fix charset $this->database->charSet = $this->dbCharset; // crmv@124454 //crmv@fix charset end //crmv@fix assoc $this->database->SetFetchMode(ADODB_FETCH_DEFAULT); //crmv@fix assoc end //crmv@datadict add $this->datadict = NewDataDictionary($this->database); //crmv@datadict add end // crmv@193619 if (!empty($this->clientFlags)) { $this->database->clientFlags = $this->clientFlags; } // crmv@193619e // crmv@65455 if ($this->usePersistent) { $this->database->PConnect($this->dbHostName, $this->userName, $this->userPassword, $this->dbName); } else { $this->database->Connect($this->dbHostName, $this->userName, $this->userPassword, $this->dbName); } // crmv@65455e $this->database->LogSQL($this->enableSQLlog); // 'SET NAMES UTF8' needs to be executed even if database has default CHARSET UTF8 // as mysql server might be running with different charset! // We will notice problem reading UTF8 characters otherwise. if($this->isdb_default_utf8_charset) { //crmv@fix charset mysql if ($this->isMySQL()) $this->executeSetNamesUTF8SQL(true); //crmv@fix charset mysql end } // crmv@146653 // executes special initialization for different DBs if ($this->isMySQL()) { $this->database->Execute("SET SESSION sql_mode = ''"); // disable sql mode } elseif ($this->isMssql()) { $this->database->Execute("SET DATEFORMAT 'Ymd'"); // crmv@155585 if ($this->dbType != 'mssqlnative') { $this->database->Execute("SET TEXTSIZE 1000000"); // crmv@129940 } // crmv@155585e } // crmv@146653e //crmv@db fix $this->avoidPreparedSql = true; //crmv@db fix end //crmv@db config switch if ($dbconfig['db_dieOnError'] === true){ $this->dieOnError = true; } //crmv@db config switch end } /** * Constructor */ function __construct($dbtype='',$host='',$dbname='',$username='',$passwd='', $charset='') { // crmv@124454 global $currentModule; $this->log = LoggerManager::getLogger('PearDatabase_'. $currentModule); $this->resetSettings($dbtype,$host,$dbname,$username,$passwd,$charset); // crmv@124454 // Initialize performance parameters $this->isdb_default_utf8_charset = PerformancePrefs::getBoolean('DB_DEFAULT_CHARSET_UTF8'); $this->enableCache = PerformancePrefs::getBoolean('CACHE_QUERY_RESULT', false); // END if(!isset($this->dbType)) { $this->println("ADODB Connect : DBType not specified"); return; } // Initialize the cache object to use. if(isset($this->enableCache) && $this->enableCache) { $this->__setCacheInstance(new PearDatabaseCache($this)); } // END } function resetSettings($dbtype,$host,$dbname,$username,$passwd, $charset = ''){ // crmv@124454 global $dbconfig, $dbconfigoption; if($host == '') { $this->disconnect(); $this->setDatabaseType($dbconfig['db_type']); $this->setUserName($dbconfig['db_username']); $this->setUserPassword($dbconfig['db_password']); //crmv@56443 if($this->dbType == 'mysqli' || $this->dbType == 'mssqlnative'){ // crmv@155585 $this->setDatabaseHost( $dbconfig['db_server']); } else { $this->setDatabaseHost( $dbconfig['db_hostname']); } //crmv@56443e $this->setDatabaseName($dbconfig['db_name']); $this->dbOptions = $dbconfigoption; if($dbconfig['log_sql']) $this->enableSQLlog = ($dbconfig['log_sql'] == true); } else { $this->disconnect(); $this->setDatabaseType($dbtype); $this->setDatabaseName($dbname); $this->setUserName($username); $this->setUserPassword($passwd); $this->setDatabaseHost( $host); } // crmv@124454 if ($charset) { $this->setCharset($charset); } elseif (isset($dbconfig['db_charset'])) { $this->setCharset($dbconfig['db_charset']); } // crmv@124454e } function quote($string){ return $this->database->qstr($string); } //crmv@offline function disconnect() { $this->println("ADODB disconnect"); if(isset($this->database)){ $this->database->disconnect(); unset($this->database); } } //crmv@offline end function setDebug($value) { $this->database->debug = $value; } // ADODB newly added methods function createTables($schemaFile, $dbHostName=false, $userName=false, $userPassword=false, $dbName=false, $dbType=false) { $this->println("ADODB createTables ".$schemaFile); if($dbHostName!=false) $this->dbHostName=$dbHostName; if($userName!=false) $this->userName=$userPassword; if($userPassword!=false) $this->userPassword=$userPassword; if($dbName!=false) $this->dbName=$dbName; if($dbType!=false) $this->dbType=$dbType; $this->checkConnection(); $db = $this->database; $schema = new adoSchema( $db ); //Debug Adodb XML Schema $schema->XMLS_DEBUG = TRUE; //Debug Adodb $schema->debug = true; $sql = $schema->ParseSchema( $schemaFile ); $this->println("--------------Starting the table creation------------------"); $result = $schema->ExecuteSchema( $sql, $this->continueInstallOnError ); if($result) print $db->errorMsg(); // needs to return in a decent way $this->println("ADODB createTables ".$schemaFile." status=".$result); return $result; } function createTable($tablename, $flds) { $this->println("ADODB createTable table=".$tablename." flds=".$flds); $this->checkConnection(); $dict = NewDataDictionary($this->database); $sqlarray = $dict->CreateTableSQL($tablename, $flds); $result = $dict->ExecuteSQLArray($sqlarray); $this->println("ADODB createTable table=".$tablename." flds=".$flds." status=".$result); return $result; } function alterTable($tablename, $flds, $oper, $printError = true) { // crmv@150747 $this->println("ADODB alterTableTable table=".$tablename." flds=".$flds." oper=".$oper); $this->checkConnection(); $dict = NewDataDictionary($this->database); if($oper == 'Add_Column') { $sqlarray = $dict->AddColumnSQL($tablename, $flds); } else if($oper == 'Delete_Column') { $sqlarray = $dict->DropColumnSQL($tablename, $flds); } $this->println("sqlarray"); $this->println($sqlarray); $result = $dict->ExecuteSQLArray($sqlarray, true, $printError); // crmv@150747 $this->println("ADODB alterTableTable table=".$tablename." flds=".$flds." oper=".$oper." status=".$result); return $result; } function addColumnToTable($tablename, $columnname, $type, $extra = '') { // check if already present $cols = $this->getColumnNames($tablename); if (in_array($columnname, $cols)) { return; } $col = $columnname.' '.$type.' '.$extra; $this->alterTable($tablename, $col, 'Add_Column'); } function getColumnNames($tablename) { $this->println("ADODB getColumnNames table=".$tablename); $this->checkConnection(); //crmv@19893 if ($this->table_exist($tablename) == 0) return Array(); //crmv@19893 $adoflds = $this->database->MetaColumns($tablename); $i=0; foreach($adoflds as $fld) { $colNames[$i] = $fld->name; $i++; } return $colNames; } function formatString($tablename,$fldname, $str) { $this->checkConnection(); $adoflds = $this->database->MetaColumns($tablename); foreach ( $adoflds as $fld ) { if(strcasecmp($fld->name,$fldname)==0) { $fldtype =strtoupper($fld->type); if(strcmp($fldtype,'CHAR')==0 || strcmp($fldtype,'VARCHAR') == 0 || strcmp($fldtype,'VARCHAR2') == 0 || strcmp($fldtype,'LONGTEXT')==0 || strcmp($fldtype,'TEXT')==0) { return $this->database->Quote($str); } else if(strcmp($fldtype,'DATE') ==0 || strcmp($fldtype,'TIMESTAMP')==0) { return $this->formatDate($str); } else { return $str; } } } $this->println("format String Illegal field name ".$fldname); return $str; } function formatDate($datetime, $strip_quotes=false) { $this->checkConnection(); $db = &$this->database; //crmv@fix datetime if (trim($datetime) == ''){ $datetime = null; } //crmv@fix datetime end /* Asha: Stripping single quotes to use the date as parameter for Prepared statement */ //crmv@fix date null if($strip_quotes == true && $date !== null) { return trim($datetime, "'"); } //crmv@fix date null end return $datetime; } function getDBDateString($datecolname) { $this->checkConnection(); $db = &$this->database; //crmv@date fix $datestr = $db->SQLDate("Y-m-d H:i:s" ,$datecolname); //crmv@date fix end return $datestr; } function getUniqueID($seqname) { $this->checkConnection(); return $this->database->GenID($seqname."_seq",1); } /** * Optimized function to generate multiple ids at once. * Returns an array of ids (usually sequential, but this depends * on the database type) to be used for the INSERT queries. * Returns false if any of the id could not be generated. */ function getMultiUniqueID($seqname, $length) { $list = array(); if ($length == 0) return $list; // first iteration is standard, to be sure the table is created // this also calls the checkConnection $list[] = $this->getUniqueID($seqname); // check if some error occurred if ($list[0] === false) return false; // crmv@155585 // crmv@155585 if ($list[0] === 0) { $list[0] = $this->getUniqueID($seqname); } // crmv@155585e // return immediately if no more ids are required if ($length == 1) return $list; $table = $seqname.'_seq'; $inc = intval($length-1); // disable log $savelog = $this->database->_logsql; $this->database->_logsql = false; $connid = $this->database->_connectionID; // now do some optimized calls, according to the db type if ($this->isMysql()) { $table = str_replace('.', '`.`', $table); //crmv@185894 $rs = @$this->database->Execute("UPDATE `{$table}` SET id = LAST_INSERT_ID(id+{$inc})"); if ($rs) { if ($this->dbType == 'mysqli') { $lastid = mysqli_insert_id($connid); } else { $lastid = mysql_insert_id($connid); } $rs->Close(); } else { $lastid = 0; } if ($lastid) { // I'm sure the ids are sequential, because I issued one single query $list = array_merge($list, range($lastid-$inc+1, $lastid)); } else { $list = false; } } elseif ($this->isMssql()) { if ($this->dbType == 'mssqlnative') { sqlsrv_begin_transaction($connid); $rs = @$this->database->Execute("UPDATE {$table} WITH (tablock,holdlock) SET id = id + {$inc}"); if ($rs) { $lastid = $this->database->GetOne("SELECT id FROM {$table}"); sqlsrv_commit($connid); } else { $lastid = 0; sqlsrv_rollback($connid); } } else { $this->database->Execute('BEGIN TRANSACTION adodbseq'); $rs = @$this->database->Execute("UPDATE {$table} WITH (tablock,holdlock) SET id = id + {$inc}"); if ($rs) { $lastid = $this->database->GetOne("SELECT id FROM {$table}"); $this->database->Execute('COMMIT TRANSACTION adodbseq'); } else { $lastid = 0; $this->database->Execute('ROLLBACK TRANSACTION adodbseq'); } } if ($lastid) { // I'm sure the ids are sequential, because I issued one single query $list = array_merge($list, range($lastid-$inc+1, $lastid)); } else { $list = false; } //} elseif ($this->isOracle()) { // oracle requires some complicated stuff, like connect by level ... // better to stick with the standard loop } else { // standard fallback for ($i=1; $i<$length; $i++) { $id = $this->getUniqueID($seqname); if ($id) { $list[] = $id; } else { $list = false; break; } } } // restore the log $this->_logsql = $savelog; return $list; } function get_tables() { $this->checkConnection(); $result = & $this->database->MetaTables('TABLES'); $this->println($result); return $result; } //To get a function name with respect to the database type which escapes strings in given text function sql_escape_string($str) { //crmv@fix escape string if($this->isPostgres()) $result_data = pg_escape_string($str); elseif($this->isMysql()) //crmv@56443 crmv@89444 if (empty($this->database->_connectionID)) { $result_data = $this->mysql_escape_mimic($str); } else { if($this->dbType == 'mysqli') { $result_data = mysqli_real_escape_string($this->database->_connectionID,$str); } else { $result_data = mysql_real_escape_string($str, $this->database->_connectionID); //crmv@56973 } } //crmv@56443e crmv@89444e elseif ($this->isMssql() || $this->isOracle()){ $result_data = str_replace("'", "''", $str); } else{ $result_data = str_replace("'", "\'", $str); } return $result_data; //crmv@fix escape string end } //crmv@89444 function mysql_escape_mimic($inp) { if(is_array($inp)) return array_map(__METHOD__, $inp); if(!empty($inp) && is_string($inp)) { return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp); } return $inp; } //crmv@89444e // Function to get the last insert id based on the type of database function getLastInsertID($seqname = '') { if($this->isPostgres()) { $result = pg_query("SELECT currval('".$seqname."_seq')"); if($result) { $row = pg_fetch_row($result); $last_insert_id = $row[0]; } } else { $last_insert_id = $this->database->Insert_ID(); } return $last_insert_id; } // Function to escape the special characters in database name based on database type. function escapeDbName($dbName='') { $dbName = $this->dbName; if($this->isMySql()) { $dbName = "`{$dbName}`"; } return $dbName; } //crmv@add functions function format_columns(&$columns, $force = false){ // crmv@165479 if ($this->isMssql()){ //crmv@27811 if (is_array($columns)) array_walk_recursive($columns,'add_brakets'); else { $columns = "[$columns]"; } //crmv@27811e } elseif ($this->isOracle()){ //crmv@24791 crmv@165479 if (is_array($columns)) array_walk_recursive($columns,'add_doublequotes', $force); elseif ($force || in_array($columns,getOracleReservedWords())) $columns = '"'.$columns.'"'; //crmv@24791e crmv@165479e } //crmv@26687 elseif ($this->isMySql()){ if (is_array($columns)) array_walk_recursive($columns,'add_backtick'); else $columns = "`{$columns}`"; } //crmv@26687e } // crmv@165801 /** * like format_columns, but work on a single column only */ function format_column($column, $force = false) { $this->format_columns($column, $force); return $column; } // crmv@165801e //crmv@78573 /** * Check if a temporary table exists */ function temp_table_exist($table) { if ($this->isMysql()) { //crmv@33979 // this is the only safe way in mysql to check the existance of a temporary table $rs = $this->database->Execute("SELECT NULL FROM $table"); if ($rs){ return true; } else { //cancello la tabella temporanea così devo ricrearla // ma per quale cagione dovrei fare codesto barbatrucco? //$rs = $this->Execute("drop temporary table if exists $table"); } //crmv@33979e } elseif ($this->isMssql()) { $sql = "SELECT OBJECT_ID('tempdb..#$table') AS id"; return !!$this->GetOne($sql); } return false; } //crmv@78573e // crmv@99131 function table_exist($tablename,$temp = false, $skipCache = false){ if (($this->isMssql() || $this->isMysql()) && $temp) { //crmv@21249 return $this->temp_table_exist($tablename); // crmv@148761 } else { //crmv@47905bis //return count($this->database->MetaTables('TABLES',false,$tablename)); if ($skipCache) { $tables = array_flip($this->database->MetaTables('TABLES')); } else { $cache = Cache::getInstance('table_exist'); $tables = $cache->get(); if ($tables === false) { $tables = array_flip($this->database->MetaTables('TABLES')); $cache->set($tables); } } (isset($tables[$tablename])) ? $return = 1 : $return = 0; return $return; //crmv@47905bis e } } // crmv@99131e // crmv@103023 crmv@113804 /** * Often, when using conditions in OR, for example (WHERE field1 = 'a' OR field2 = 'b'), * performances are poor because the db engine cannot use indexes for each field. * Using 2 queries and joining the results with union sometimes gives better speed. * This function takes a sql select statement and an array of conditions (which were * orignially in OR in the query) and generates a new sql statement using unions. * If you are sure that the conditions generate non overlapping sets, specify false * in the 3rd parameter to skip the standard union merge. */ function makeUnionSelect($sql, $conditions, $distinct = true, &$params = array(), $cloneTables = array()) { if (empty($conditions)) return $sql; $repTables = array(); if (count($cloneTables) > 0 && count($conditions) > 1) { foreach ($cloneTables as $table) { for ($i=2; $i<=count($conditions); ++$i) { $newname = $table.'_'.$i; $this->cloneTemporaryTable($table, $newname); $repTables[$i-1][$table] = $newname; } } } $i = 0; $newsql = array(); foreach ($conditions as $cond) { $addSql = null; if (is_string($cond)) { // simple string condition $addSql = $sql . " AND $cond\n"; } elseif (is_array($cond)) { // sql and array of parameers $addSql = $sql . $this->convert2Sql(" AND {$cond[0]}\n",$this->flatten_array($cond[1])); } if ($addSql) { if (is_array($repTables[$i])) { $addSql = str_replace(array_keys($repTables[$i]), array_values($repTables[$i]), $addSql); } $newsql[] = $addSql; } ++$i; } if ($params && count($params) > 0) { // I have to duplicate the params too $params = $this->flatten_array(array_fill(0, count($conditions), $params)); } $join = $distinct ? "UNION" : "UNION ALL"; return implode(" $join \n", $newsql); } // crmv@103023e /** * Clone a temporary table. This method only works for MySql */ function cloneTemporaryTable($table, $newtable) { $this->query("CREATE TEMPORARY TABLE IF NOT EXISTS $newtable LIKE $table"); $this->query("TRUNCATE TABLE $newtable"); $this->query("INSERT INTO $newtable SELECT * FROM $table"); } // crmv@113804e //crmv@add functions end //crmv@offline function get_sequences(){ if ($this->isOracle()){ $sql = "select sequence_name,last_number from user_sequences"; $res = $this->query($sql); if ($res && $this->num_rows($res)>0){ while($row = $this->fetchByAssoc($res,-1,false)){ $sequences[substr($row[sequence_name],0,-4)] = $row[last_number]; } } } else { $tables_seq = $this->database->Metatables(false,false,"%_seq"); $sql = "select id from "; if (is_array($tables_seq)){ foreach ($tables_seq as $table){ $id = $this->query_result_no_html($this->query($sql.$table),0,'id'); $sequences[substr($table,0,-4)] = $id; } } } return $sequences; } //crmv@offline end // crmv@90924 function getPartitions($table) { $parts = array(); if ($this->isMysql()) { $sql = " SELECT partition_name, table_rows, partition_expression, partition_description FROM INFORMATION_SCHEMA.PARTITIONS WHERE table_schema = ? AND table_name = ? AND partition_name IS NOT NULL"; $res = $this->pquery($sql, array($this->dbName, $table)); if ($res && $this->num_rows($res) > 0) { while ($row = $this->fetchByAssoc($res, -1, false)) { $parts[] = $row; } } return $parts; } else { // TODO: other databases throw new Exception("Database type not supported"); } } function hasPartition($table, $partName) { $parts = $this->getPartitions($table); foreach ($parts as $pinfo) { if ($pinfo['partition_name'] == $partName) return true; } return false; } // crmv@90924e // crmv@197204 /** * Return possible values for each partition */ function getPartitionsValues($table) { $parts = $this->getPartitions($table); $values = array(); foreach ($parts as $pinfo) { $partitionName = $pinfo['partition_name']; if (!isset($values[$partitionName])) { $values[$partitionName] = array(); } $partitionDesc = $pinfo['partition_description']; if (!empty($partitionDesc)) { $setypes = array_map(function($v) { return trim(trim($v, "'")); }, explode(',', $partitionDesc)); $values[$partitionName] = array_merge($values[$partitionName], $setypes); } } return $values; } /** * Return true if the specified value is in any partition */ function checkPartitionValue($table, $value) { $patitionsValues = $this->getPartitionsValues($table); if (!empty($patitionsValues)) { $allValues = $this->flatten_array($patitionsValues); return in_array($value, $allValues); } return true; } // crmv@197204e // crmv@185894 function getSlaveObject(string $slaveFunction) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { return $slaveHandler->getPearDatabaseObject(); } } return $this; } function ErrorMsgSlave(string $slaveFunction) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); return $adbSlave->database->ErrorMsg(); } } return $this->database->ErrorMsg(); } function getUniqueIDSlave(string $slaveFunction, $seqname) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); return $adbSlave->getUniqueID($seqname); } } return $this->getUniqueID($seqname); } function getMultiUniqueIDSlave(string $slaveFunction, $seqname, $length) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); return $adbSlave->getMultiUniqueID($seqname, $length); } } return $this->getMultiUniqueID($seqname, $length); } function bulkInsertSlave(string $slaveFunction, $table, $columns, $rows = array(), $chunkSize = 100, $ignore = false) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); return $adbSlave->bulkInsert($table, $columns, $rows, $chunkSize, $ignore); } } return $this->bulkInsert($table, $columns, $rows, $chunkSize, $ignore); } function DropTableSQLSlave(string $slaveFunction, $tabname, $schema='') { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); if (!empty($schema)) $adbSlave->datadict->schema = $schema; $return = $adbSlave->datadict->DropTableSQL($tabname); if (!empty($schema)) $adbSlave->datadict->schema = ''; return $return; } } return $this->datadict->DropTableSQL($tabname); } function CreateIndexSQLSlave(string $slaveFunction, $idxname, $tabname, $flds, $idxoptions = false) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); return $adbSlave->datadict->CreateIndexSQL($idxname, $tabname, $flds, $idxoptions); } } return $this->datadict->CreateIndexSQL($idxname, $tabname, $flds, $idxoptions); } function ExecuteSQLArraySlave(string $slaveFunction, $sql, $continueOnError = true, $printError = true) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); global $globSkipClearCache; $globSkipClearCache = true; $result = $adbSlave->datadict->ExecuteSQLArray($sql, $continueOnError, $printError); $globSkipClearCache = false; return $result; } } return $this->datadict->ExecuteSQLArray($sql, $continueOnError, $printError); } function querySlave(string $slaveFunction, string $sql, bool $dieOnError=false, string $msg='', bool $temp=false) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); $adbSlave->slave = true; $result = $adbSlave->pquery($sql, $dieOnError, $msg, $temp); $adbSlave->slave = false; return $result; } } // se sla_handler disattivo, funzione non supportata o connessione assente vado sul db master return $this->pquery($sql, $dieOnError, $msg, $temp); } function pquerySlave(string $slaveFunction, string $sql, array $params, bool $dieOnError=false, string $msg='', bool $temp=false) { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); $adbSlave->slave = true; $result = $adbSlave->pquery($sql, $params, $dieOnError, $msg, $temp); $adbSlave->slave = false; return $result; } } return $this->pquery($sql, $params, $dieOnError, $msg, $temp); } function limitQuerySlave(string $slaveFunction, string $sql, int $start, int $count, bool $dieOnError=false, string $msg='') { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); $adbSlave->slave = true; $result = $adbSlave->limitQuery($sql, $start, $count, $dieOnError, $msg); $adbSlave->slave = false; return $result; } } return $this->limitQuery($sql, $start, $count, $dieOnError, $msg); } function limitpQuerySlave(string $slaveFunction, string $sql, int $start, int $count, array $params=array(), bool $dieOnError=false, string $msg='') { $slaveHandler = SlaveHandler::getInstance(); if ($slaveHandler->isActive($slaveFunction)) { $success = $slaveHandler->checkDatabaseConnection(); if ($success) { $adbSlave = $slaveHandler->getPearDatabaseObject(); $adbSlave->slave = true; $result = $adbSlave->limitpQuery($sql, $start, $count, $params, $dieOnError, $msg); $adbSlave->slave = false; return $result; } } return $this->limitpQuery($sql, $start, $count, $params, $dieOnError, $msg); } // crmv@185894e // crmv@198024 /** * Return true if the database supports json functions */ public function supportsJSON() { $info = $this->database->ServerInfo(); if ($this->isMysql()) { return version_compare($info['version'], '5.7.8', '>='); } elseif ($this->isMssql()) { return version_compare($info['version'], '13.0', '>='); } elseif ($this->isOracle()) { return version_compare($info['version'], '12.1.0.2', '>='); } return false; } /** * Generate an sql expression to extract values from a json-encoded column or null if not a valid json * Only mysql has been tested */ public function jsonValue($column, $path, $emptyCheck = true) { if ($this->isMysql()) { $sqlcolumn = "JSON_UNQUOTE(JSON_EXTRACT($column, '{$path}'))"; } elseif ($this->isOracle() || $this->isMsSql()) { // not tested! $sqlcolumn = "JSON_VALUE($column, '{$path}')"; } if ($emptyCheck) { $sqlcolumn = "IF($column IS NULL OR $column = '', '', $sqlcolumn)"; } return $sqlcolumn; } // crmv@198024 } /* End of class */ if(empty($adb)) { $adb = new PearDatabase(); $adb->connect(); }