mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-04 01:01:41 +09:00
1395 lines
31 KiB
PHP
1395 lines
31 KiB
PHP
<?php
|
|
/* Copyright (C) NAVER <http://www.navercorp.com> */
|
|
|
|
if(!defined('__XE_LOADED_DB_CLASS__'))
|
|
{
|
|
define('__XE_LOADED_DB_CLASS__', 1);
|
|
|
|
require(_XE_PATH_ . 'classes/xml/xmlquery/DBParser.class.php');
|
|
require(_XE_PATH_ . 'classes/xml/xmlquery/QueryParser.class.php');
|
|
require(_XE_PATH_ . 'classes/xml/xmlquery/argument/Argument.class.php');
|
|
require(_XE_PATH_ . 'classes/xml/xmlquery/argument/SortArgument.class.php');
|
|
require(_XE_PATH_ . 'classes/xml/xmlquery/argument/ConditionArgument.class.php');
|
|
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/Expression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/SelectExpression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/InsertExpression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/UpdateExpression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/ClickCountExpression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/Table.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/JoinTable.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/CubridTableWithHint.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/MysqlTableWithHint.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/MssqlTableWithHint.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/table/IndexHint.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/condition/ConditionGroup.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/condition/Condition.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/condition/ConditionWithArgument.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/condition/ConditionWithoutArgument.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/condition/ConditionSubquery.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/expression/StarExpression.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/order/OrderByColumn.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/limit/Limit.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/Query.class.php');
|
|
require(_XE_PATH_ . 'classes/db/queryparts/Subquery.class.php');
|
|
}
|
|
|
|
/**
|
|
* - DB parent class
|
|
* - usage of db in XE is via xml
|
|
* - there are 2 types of xml - query xml, schema xml
|
|
* - in case of query xml, DB::executeQuery() method compiles xml file into php code and then execute it
|
|
* - query xml has unique query id, and will be created in module
|
|
* - queryid = module_name.query_name
|
|
*
|
|
* @author NAVER (developers@xpressengine.com)
|
|
* @package /classes/db
|
|
* @version 0.1
|
|
*/
|
|
class DB
|
|
{
|
|
|
|
static $isSupported = FALSE;
|
|
|
|
/**
|
|
* priority of DBMS
|
|
* @var array
|
|
*/
|
|
var $priority_dbms = array(
|
|
'mysqli' => 6,
|
|
'mysqli_innodb' => 5,
|
|
'mysql' => 4,
|
|
'mysql_innodb' => 3,
|
|
'cubrid' => 2,
|
|
'mssql' => 1
|
|
);
|
|
|
|
/**
|
|
* count cache path
|
|
* @var string
|
|
*/
|
|
var $count_cache_path = 'files/cache/db';
|
|
|
|
/**
|
|
* operations for condition
|
|
* @var array
|
|
*/
|
|
var $cond_operation = array(
|
|
'equal' => '=',
|
|
'more' => '>=',
|
|
'excess' => '>',
|
|
'less' => '<=',
|
|
'below' => '<',
|
|
'notequal' => '<>',
|
|
'notnull' => 'is not null',
|
|
'null' => 'is null',
|
|
);
|
|
|
|
/**
|
|
* master database connection string
|
|
* @var array
|
|
*/
|
|
var $master_db = NULL;
|
|
|
|
/**
|
|
* array of slave databases connection strings
|
|
* @var array
|
|
*/
|
|
var $slave_db = NULL;
|
|
var $result = NULL;
|
|
|
|
/**
|
|
* error code (0 means no error)
|
|
* @var int
|
|
*/
|
|
var $errno = 0;
|
|
|
|
/**
|
|
* error message
|
|
* @var string
|
|
*/
|
|
var $errstr = '';
|
|
|
|
/**
|
|
* query string of latest executed query
|
|
* @var string
|
|
*/
|
|
var $query = '';
|
|
var $connection = '';
|
|
|
|
/**
|
|
* elapsed time of latest executed query
|
|
* @var int
|
|
*/
|
|
var $elapsed_time = 0;
|
|
|
|
/**
|
|
* elapsed time of latest executed DB class
|
|
* @var int
|
|
*/
|
|
var $elapsed_dbclass_time = 0;
|
|
|
|
/**
|
|
* transaction flag
|
|
* @var boolean
|
|
*/
|
|
var $transaction_started = FALSE;
|
|
var $is_connected = FALSE;
|
|
|
|
/**
|
|
* returns enable list in supported dbms list
|
|
* will be written by classes/DB/DB***.class.php
|
|
* @var array
|
|
*/
|
|
var $supported_list = array();
|
|
|
|
/**
|
|
* location of query cache
|
|
* @var string
|
|
*/
|
|
var $cache_file = 'files/cache/queries/';
|
|
|
|
/**
|
|
* stores database type: 'mysql','cubrid','mssql' etc. or 'db' when database is not yet set
|
|
* @var string
|
|
*/
|
|
var $db_type;
|
|
|
|
/**
|
|
* flag to decide if class prepared statements or not (when supported); can be changed from db.config.info
|
|
* @var string
|
|
*/
|
|
var $use_prepared_statements;
|
|
|
|
/**
|
|
* leve of transaction
|
|
* @var unknown
|
|
*/
|
|
private $transactionNestedLevel = 0;
|
|
|
|
/**
|
|
* returns instance of certain db type
|
|
* @param string $db_type type of db
|
|
* @return DB return DB object instance
|
|
*/
|
|
function &getInstance($db_type = NULL)
|
|
{
|
|
if(!$db_type)
|
|
{
|
|
$db_type = Context::getDBType();
|
|
}
|
|
if(!$db_type && Context::isInstalled())
|
|
{
|
|
return new Object(-1, 'msg_db_not_setted');
|
|
}
|
|
|
|
if(!isset($GLOBALS['__DB__']))
|
|
{
|
|
$GLOBALS['__DB__'] = array();
|
|
}
|
|
if(!isset($GLOBALS['__DB__'][$db_type]))
|
|
{
|
|
$class_name = 'DB' . ucfirst($db_type);
|
|
$class_file = _XE_PATH_ . "classes/db/$class_name.class.php";
|
|
if(!file_exists($class_file))
|
|
{
|
|
return new Object(-1, 'msg_db_not_setted');
|
|
}
|
|
|
|
// get a singletone instance of the database driver class
|
|
require_once($class_file);
|
|
$GLOBALS['__DB__'][$db_type] = call_user_func(array($class_name, 'create'));
|
|
$GLOBALS['__DB__'][$db_type]->db_type = $db_type;
|
|
}
|
|
|
|
return $GLOBALS['__DB__'][$db_type];
|
|
}
|
|
|
|
/**
|
|
* returns instance of db
|
|
* @return DB return DB object instance
|
|
*/
|
|
function create()
|
|
{
|
|
return new DB;
|
|
}
|
|
|
|
/**
|
|
* constructor
|
|
* @return void
|
|
*/
|
|
function DB()
|
|
{
|
|
$this->count_cache_path = _XE_PATH_ . $this->count_cache_path;
|
|
$this->cache_file = _XE_PATH_ . $this->cache_file;
|
|
}
|
|
|
|
/**
|
|
* returns list of supported dbms list
|
|
* this list return by directory list
|
|
* check by instance can creatable
|
|
* @return array return supported DBMS list
|
|
*/
|
|
function getSupportedList()
|
|
{
|
|
$oDB = new DB();
|
|
return $oDB->_getSupportedList();
|
|
}
|
|
|
|
/**
|
|
* returns enable list in supported dbms list
|
|
* this list return by child class
|
|
* @return array return enable DBMS list in supported dbms list
|
|
*/
|
|
function getEnableList()
|
|
{
|
|
if(!$this->supported_list)
|
|
{
|
|
$oDB = new DB();
|
|
$this->supported_list = $oDB->_getSupportedList();
|
|
}
|
|
|
|
$enableList = array();
|
|
if(is_array($this->supported_list))
|
|
{
|
|
foreach($this->supported_list AS $key => $value)
|
|
{
|
|
if($value->enable)
|
|
{
|
|
$enableList[] = $value;
|
|
}
|
|
}
|
|
}
|
|
return $enableList;
|
|
}
|
|
|
|
/**
|
|
* returns list of disable in supported dbms list
|
|
* this list return by child class
|
|
* @return array return disable DBMS list in supported dbms list
|
|
*/
|
|
function getDisableList()
|
|
{
|
|
if(!$this->supported_list)
|
|
{
|
|
$oDB = new DB();
|
|
$this->supported_list = $oDB->_getSupportedList();
|
|
}
|
|
|
|
$disableList = array();
|
|
if(is_array($this->supported_list))
|
|
{
|
|
foreach($this->supported_list AS $key => $value)
|
|
{
|
|
if(!$value->enable)
|
|
{
|
|
$disableList[] = $value;
|
|
}
|
|
}
|
|
}
|
|
return $disableList;
|
|
}
|
|
|
|
/**
|
|
* returns list of supported dbms list
|
|
* this method is private
|
|
* @return array return supported DBMS list
|
|
*/
|
|
function _getSupportedList()
|
|
{
|
|
static $get_supported_list = '';
|
|
if(is_array($get_supported_list))
|
|
{
|
|
$this->supported_list = $get_supported_list;
|
|
return $this->supported_list;
|
|
}
|
|
$get_supported_list = array();
|
|
$db_classes_path = _XE_PATH_ . "classes/db/";
|
|
$filter = "/^DB([^\.]+)\.class\.php/i";
|
|
$supported_list = FileHandler::readDir($db_classes_path, $filter, TRUE);
|
|
|
|
// after creating instance of class, check is supported
|
|
for($i = 0; $i < count($supported_list); $i++)
|
|
{
|
|
$db_type = $supported_list[$i];
|
|
|
|
$class_name = sprintf("DB%s%s", strtoupper(substr($db_type, 0, 1)), strtolower(substr($db_type, 1)));
|
|
$class_file = sprintf(_XE_PATH_ . "classes/db/%s.class.php", $class_name);
|
|
if(!file_exists($class_file))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
unset($oDB);
|
|
require_once($class_file);
|
|
$tmp_fn = create_function('', "return new {$class_name}();");
|
|
$oDB = $tmp_fn();
|
|
|
|
if(!$oDB)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$obj = new stdClass;
|
|
$obj->db_type = $db_type;
|
|
$obj->enable = $oDB->isSupported() ? TRUE : FALSE;
|
|
|
|
$get_supported_list[] = $obj;
|
|
}
|
|
|
|
// sort
|
|
@usort($get_supported_list, array($this, '_sortDBMS'));
|
|
|
|
$this->supported_list = $get_supported_list;
|
|
return $this->supported_list;
|
|
}
|
|
|
|
/**
|
|
* sort dbms as priority
|
|
*/
|
|
function _sortDBMS($a, $b)
|
|
{
|
|
if(!isset($this->priority_dbms[$a->db_type]))
|
|
{
|
|
$priority_a = 0;
|
|
}
|
|
else
|
|
{
|
|
$priority_a = $this->priority_dbms[$a->db_type];
|
|
}
|
|
|
|
if(!isset($this->priority_dbms[$b->db_type]))
|
|
{
|
|
$priority_b = 0;
|
|
}
|
|
else
|
|
{
|
|
$priority_b = $this->priority_dbms[$b->db_type];
|
|
}
|
|
|
|
if($priority_a == $priority_b)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return ($priority_a > $priority_b) ? -1 : 1;
|
|
}
|
|
|
|
/**
|
|
* Return dbms supportable status
|
|
* The value is set in the child class
|
|
* @return boolean true: is supported, false: is not supported
|
|
*/
|
|
function isSupported()
|
|
{
|
|
return self::$isSupported;
|
|
}
|
|
|
|
/**
|
|
* Return connected status
|
|
* @param string $type master or slave
|
|
* @param int $indx key of server list
|
|
* @return boolean true: connected, false: not connected
|
|
*/
|
|
function isConnected($type = 'master', $indx = 0)
|
|
{
|
|
if($type == 'master')
|
|
{
|
|
return $this->master_db["is_connected"] ? TRUE : FALSE;
|
|
}
|
|
else
|
|
{
|
|
return $this->slave_db[$indx]["is_connected"] ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* start recording log
|
|
* @param string $query query string
|
|
* @return void
|
|
*/
|
|
function actStart($query)
|
|
{
|
|
$this->setError(0, 'success');
|
|
$this->query = $query;
|
|
$this->act_start = getMicroTime();
|
|
$this->elapsed_time = 0;
|
|
}
|
|
|
|
/**
|
|
* finish recording log
|
|
* @return void
|
|
*/
|
|
function actFinish()
|
|
{
|
|
if(!$this->query)
|
|
{
|
|
return;
|
|
}
|
|
$this->act_finish = getMicroTime();
|
|
$elapsed_time = $this->act_finish - $this->act_start;
|
|
$this->elapsed_time = $elapsed_time;
|
|
$GLOBALS['__db_elapsed_time__'] += $elapsed_time;
|
|
|
|
$site_module_info = Context::get('site_module_info');
|
|
$log = array();
|
|
$log['query'] = $this->query;
|
|
$log['elapsed_time'] = $elapsed_time;
|
|
$log['connection'] = $this->connection;
|
|
$log['query_id'] = $this->query_id;
|
|
$log['module'] = $site_module_info->module;
|
|
$log['act'] = Context::get('act');
|
|
$log['time'] = date('Y-m-d H:i:s');
|
|
|
|
$bt = version_compare(PHP_VERSION, '5.3.6', '>=') ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) : debug_backtrace();
|
|
|
|
foreach($bt as $no => $call)
|
|
{
|
|
if($call['function'] == 'executeQuery' || $call['function'] == 'executeQueryArray')
|
|
{
|
|
$call_no = $no;
|
|
$call_no++;
|
|
$log['called_file'] = $bt[$call_no]['file'].':'.$bt[$call_no]['line'];
|
|
$log['called_file'] = str_replace(_XE_PATH_ , '', $log['called_file']);
|
|
$call_no++;
|
|
$log['called_method'] = $bt[$call_no]['class'].$bt[$call_no]['type'].$bt[$call_no]['function'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// leave error log if an error occured (if __DEBUG_DB_OUTPUT__ is defined)
|
|
if($this->isError())
|
|
{
|
|
$log['result'] = 'Failed';
|
|
$log['errno'] = $this->errno;
|
|
$log['errstr'] = $this->errstr;
|
|
|
|
if(__DEBUG_DB_OUTPUT__ == 1)
|
|
{
|
|
$debug_file = _XE_PATH_ . "files/_debug_db_query.php";
|
|
$buff = array();
|
|
if(!file_exists($debug_file))
|
|
{
|
|
$buff[] = '<?php exit(); ?' . '>';
|
|
}
|
|
$buff[] = print_r($log, TRUE);
|
|
@file_put_contents($log_file, implode("\n", $buff) . "\n\n", FILE_APPEND|LOCK_EX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$log['result'] = 'Success';
|
|
}
|
|
|
|
$this->setQueryLog($log);
|
|
|
|
$log_args = new stdClass;
|
|
$log_args->query = $this->query;
|
|
$log_args->query_id = $this->query_id;
|
|
$log_args->caller = $log['called_method'] . '() in ' . $log['called_file'];
|
|
$log_args->connection = $log['connection'];
|
|
writeSlowlog('query', $elapsed_time, $log_args);
|
|
}
|
|
|
|
/**
|
|
* set query debug log
|
|
* @param array $log values set query debug
|
|
* @return void
|
|
*/
|
|
function setQueryLog($log)
|
|
{
|
|
$GLOBALS['__db_queries__'][] = $log;
|
|
}
|
|
|
|
/**
|
|
* set error
|
|
* @param int $errno error code
|
|
* @param string $errstr error message
|
|
* @return void
|
|
*/
|
|
function setError($errno = 0, $errstr = 'success')
|
|
{
|
|
$this->errno = $errno;
|
|
$this->errstr = $errstr;
|
|
}
|
|
|
|
/**
|
|
* Return error status
|
|
* @return boolean true: error, false: no error
|
|
*/
|
|
function isError()
|
|
{
|
|
return ($this->errno !== 0);
|
|
}
|
|
|
|
/**
|
|
* Returns object of error info
|
|
* @return object object of error
|
|
*/
|
|
function getError()
|
|
{
|
|
$this->errstr = Context::convertEncodingStr($this->errstr);
|
|
return new Object($this->errno, $this->errstr);
|
|
}
|
|
|
|
/**
|
|
* Execute Query that result of the query xml file
|
|
* This function finds xml file or cache file of $query_id, compiles it and then execute it
|
|
* @param string $query_id query id (module.queryname)
|
|
* @param array|object $args arguments for query
|
|
* @param array $arg_columns column list. if you want get specific colums from executed result, add column list to $arg_columns
|
|
* @return object result of query
|
|
*/
|
|
function executeQuery($query_id, $args = NULL, $arg_columns = NULL, $type = NULL)
|
|
{
|
|
static $cache_file = array();
|
|
|
|
if(!$query_id)
|
|
{
|
|
return new Object(-1, 'msg_invalid_queryid');
|
|
}
|
|
if(!$this->db_type)
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->actDBClassStart();
|
|
|
|
$this->query_id = $query_id;
|
|
|
|
if(!isset($cache_file[$query_id]) || !file_exists($cache_file[$query_id]))
|
|
{
|
|
$id_args = explode('.', $query_id);
|
|
if(count($id_args) == 2)
|
|
{
|
|
$target = 'modules';
|
|
$module = $id_args[0];
|
|
$id = $id_args[1];
|
|
}
|
|
elseif(count($id_args) == 3)
|
|
{
|
|
$target = $id_args[0];
|
|
$typeList = array('addons' => 1, 'widgets' => 1);
|
|
if(!isset($typeList[$target]))
|
|
{
|
|
$this->actDBClassFinish();
|
|
return;
|
|
}
|
|
$module = $id_args[1];
|
|
$id = $id_args[2];
|
|
}
|
|
if(!$target || !$module || !$id)
|
|
{
|
|
$this->actDBClassFinish();
|
|
return new Object(-1, 'msg_invalid_queryid');
|
|
}
|
|
|
|
$xml_file = sprintf('%s%s/%s/queries/%s.xml', _XE_PATH_, $target, $module, $id);
|
|
if(!file_exists($xml_file))
|
|
{
|
|
$this->actDBClassFinish();
|
|
return new Object(-1, 'msg_invalid_queryid');
|
|
}
|
|
|
|
// look for cache file
|
|
$cache_file[$query_id] = $this->checkQueryCacheFile($query_id, $xml_file);
|
|
}
|
|
$result = $this->_executeQuery($cache_file[$query_id], $args, $query_id, $arg_columns, $type);
|
|
|
|
$this->actDBClassFinish();
|
|
// execute query
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Look for query cache file
|
|
* @param string $query_id query id for finding
|
|
* @param string $xml_file original xml query file
|
|
* @return string cache file
|
|
*/
|
|
function checkQueryCacheFile($query_id, $xml_file)
|
|
{
|
|
// first try finding cache file
|
|
$cache_file = sprintf('%s%s%s.%s.%s.cache.php', _XE_PATH_, $this->cache_file, $query_id, __ZBXE_VERSION__, $this->db_type);
|
|
|
|
$cache_time = -1;
|
|
if(file_exists($cache_file))
|
|
{
|
|
$cache_time = filemtime($cache_file);
|
|
}
|
|
|
|
// if there is no cache file or is not new, find original xml query file and parse it
|
|
if($cache_time < filemtime($xml_file) || $cache_time < filemtime(_XE_PATH_ . 'classes/db/DB.class.php') || $cache_time < filemtime(_XE_PATH_ . 'classes/xml/XmlQueryParser.class.php'))
|
|
{
|
|
require_once(_XE_PATH_ . 'classes/xml/XmlQueryParser.class.php');
|
|
$oParser = new XmlQueryParser();
|
|
$oParser->parse($query_id, $xml_file, $cache_file);
|
|
}
|
|
|
|
return $cache_file;
|
|
}
|
|
|
|
/**
|
|
* Execute query and return the result
|
|
* @param string $cache_file cache file of query
|
|
* @param array|object $source_args arguments for query
|
|
* @param string $query_id query id
|
|
* @param array $arg_columns column list. if you want get specific colums from executed result, add column list to $arg_columns
|
|
* @return object result of query
|
|
*/
|
|
function _executeQuery($cache_file, $source_args, $query_id, $arg_columns, $type)
|
|
{
|
|
global $lang;
|
|
|
|
if(!in_array($type, array('master','slave'))) $type = 'slave';
|
|
|
|
if(!file_exists($cache_file))
|
|
{
|
|
return new Object(-1, 'msg_invalid_queryid');
|
|
}
|
|
|
|
if($source_args)
|
|
{
|
|
$args = clone $source_args;
|
|
}
|
|
|
|
$output = include($cache_file);
|
|
|
|
if((is_a($output, 'Object') || is_subclass_of($output, 'Object')) && !$output->toBool())
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
// execute appropriate query
|
|
switch($output->getAction())
|
|
{
|
|
case 'insert' :
|
|
case 'insert-select' :
|
|
$this->resetCountCache($output->tables);
|
|
$output = $this->_executeInsertAct($output);
|
|
break;
|
|
case 'update' :
|
|
$this->resetCountCache($output->tables);
|
|
$output = $this->_executeUpdateAct($output);
|
|
break;
|
|
case 'delete' :
|
|
$this->resetCountCache($output->tables);
|
|
$output = $this->_executeDeleteAct($output);
|
|
break;
|
|
case 'select' :
|
|
$arg_columns = is_array($arg_columns) ? $arg_columns : array();
|
|
$output->setColumnList($arg_columns);
|
|
$connection = $this->_getConnection($type);
|
|
$output = $this->_executeSelectAct($output, $connection);
|
|
break;
|
|
}
|
|
|
|
if($this->isError())
|
|
{
|
|
$output = $this->getError();
|
|
}
|
|
else if(!is_a($output, 'Object') && !is_subclass_of($output, 'Object'))
|
|
{
|
|
$output = new Object();
|
|
}
|
|
$output->add('_query', $this->query);
|
|
$output->add('_elapsed_time', sprintf("%0.5f", $this->elapsed_time));
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Returns counter cache data
|
|
* @param array|string $tables tables to get data
|
|
* @param string $condition condition to get data
|
|
* @return int count of cache data
|
|
*/
|
|
function getCountCache($tables, $condition)
|
|
{
|
|
return FALSE;
|
|
/*
|
|
if(!$tables)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if(!is_dir($this->count_cache_path))
|
|
{
|
|
return FileHandler::makeDir($this->count_cache_path);
|
|
}
|
|
|
|
$condition = md5($condition);
|
|
|
|
if(!is_array($tables))
|
|
{
|
|
$tables_str = $tables;
|
|
}
|
|
else
|
|
{
|
|
$tables_str = implode('.', $tables);
|
|
}
|
|
|
|
$cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str);
|
|
FileHandler::makeDir($cache_path);
|
|
|
|
$cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition);
|
|
if(!file_exists($cache_filename))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
$cache_mtime = filemtime($cache_filename);
|
|
|
|
if(!is_array($tables))
|
|
{
|
|
$tables = array($tables);
|
|
}
|
|
foreach($tables as $alias => $table)
|
|
{
|
|
$table_filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table);
|
|
if(!file_exists($table_filename) || filemtime($table_filename) > $cache_mtime)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
$count = (int) FileHandler::readFile($cache_filename);
|
|
return $count;
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Save counter cache data
|
|
* @param array|string $tables tables to save data
|
|
* @param string $condition condition to save data
|
|
* @param int $count count of cache data to save
|
|
* @return void
|
|
*/
|
|
function putCountCache($tables, $condition, $count = 0)
|
|
{
|
|
return FALSE;
|
|
/*
|
|
if(!$tables)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if(!is_dir($this->count_cache_path))
|
|
{
|
|
return FileHandler::makeDir($this->count_cache_path);
|
|
}
|
|
|
|
$condition = md5($condition);
|
|
|
|
if(!is_array($tables))
|
|
{
|
|
$tables_str = $tables;
|
|
}
|
|
else
|
|
{
|
|
$tables_str = implode('.', $tables);
|
|
}
|
|
|
|
$cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str);
|
|
FileHandler::makeDir($cache_path);
|
|
|
|
$cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition);
|
|
|
|
FileHandler::writeFile($cache_filename, $count);
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Reset counter cache data
|
|
* @param array|string $tables tables to reset cache data
|
|
* @return boolean true: success, false: failed
|
|
*/
|
|
function resetCountCache($tables)
|
|
{
|
|
return FALSE;
|
|
/*
|
|
if(!$tables)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return FileHandler::makeDir($this->count_cache_path);
|
|
|
|
if(!is_array($tables))
|
|
{
|
|
$tables = array($tables);
|
|
}
|
|
foreach($tables as $alias => $table)
|
|
{
|
|
$filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table);
|
|
FileHandler::removeFile($filename);
|
|
FileHandler::writeFile($filename, '');
|
|
}
|
|
|
|
return TRUE;
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Drop tables
|
|
* @param string $table_name
|
|
* @return void
|
|
*/
|
|
function dropTable($table_name)
|
|
{
|
|
if(!$table_name)
|
|
{
|
|
return;
|
|
}
|
|
$query = sprintf("drop table %s%s", $this->prefix, $table_name);
|
|
$this->_query($query);
|
|
}
|
|
|
|
/**
|
|
* Return select query string
|
|
* @param object $query
|
|
* @param boolean $with_values
|
|
* @return string
|
|
*/
|
|
function getSelectSql($query, $with_values = TRUE)
|
|
{
|
|
$select = $query->getSelectString($with_values);
|
|
if($select == '')
|
|
{
|
|
return new Object(-1, "Invalid query");
|
|
}
|
|
$select = 'SELECT ' . $select;
|
|
|
|
$from = $query->getFromString($with_values);
|
|
if($from == '')
|
|
{
|
|
return new Object(-1, "Invalid query");
|
|
}
|
|
$from = ' FROM ' . $from;
|
|
|
|
$where = $query->getWhereString($with_values);
|
|
if($where != '')
|
|
{
|
|
$where = ' WHERE ' . $where;
|
|
}
|
|
|
|
$tableObjects = $query->getTables();
|
|
$index_hint_list = '';
|
|
foreach($tableObjects as $tableObject)
|
|
{
|
|
if(is_a($tableObject, 'CubridTableWithHint'))
|
|
{
|
|
$index_hint_list .= $tableObject->getIndexHintString() . ', ';
|
|
}
|
|
}
|
|
$index_hint_list = substr($index_hint_list, 0, -2);
|
|
if($index_hint_list != '')
|
|
{
|
|
$index_hint_list = 'USING INDEX ' . $index_hint_list;
|
|
}
|
|
|
|
$groupBy = $query->getGroupByString();
|
|
if($groupBy != '')
|
|
{
|
|
$groupBy = ' GROUP BY ' . $groupBy;
|
|
}
|
|
|
|
$orderBy = $query->getOrderByString();
|
|
if($orderBy != '')
|
|
{
|
|
$orderBy = ' ORDER BY ' . $orderBy;
|
|
}
|
|
|
|
$limit = $query->getLimitString();
|
|
if($limit != '')
|
|
{
|
|
$limit = ' LIMIT ' . $limit;
|
|
}
|
|
|
|
return $select . ' ' . $from . ' ' . $where . ' ' . $index_hint_list . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit;
|
|
}
|
|
|
|
/**
|
|
* Given a SELECT statement that uses click count
|
|
* returns the corresponding update sql string
|
|
* for databases that don't have click count support built in
|
|
* (aka all besides CUBRID)
|
|
*
|
|
* Function does not check if click count columns exist!
|
|
* You must call $query->usesClickCount() before using this function
|
|
*
|
|
* @param $queryObject
|
|
*/
|
|
function getClickCountQuery($queryObject)
|
|
{
|
|
$new_update_columns = array();
|
|
$click_count_columns = $queryObject->getClickCountColumns();
|
|
foreach($click_count_columns as $click_count_column)
|
|
{
|
|
$click_count_column_name = $click_count_column->column_name;
|
|
|
|
$increase_by_1 = new Argument($click_count_column_name, null);
|
|
$increase_by_1->setColumnOperation('+');
|
|
$increase_by_1->ensureDefaultValue(1);
|
|
|
|
$update_expression = new UpdateExpression($click_count_column_name, $increase_by_1);
|
|
$new_update_columns[] = $update_expression;
|
|
}
|
|
$queryObject->columns = $new_update_columns;
|
|
return $queryObject;
|
|
}
|
|
|
|
/**
|
|
* Return delete query string
|
|
* @param object $query
|
|
* @param boolean $with_values
|
|
* @param boolean $with_priority
|
|
* @return string
|
|
*/
|
|
function getDeleteSql($query, $with_values = TRUE, $with_priority = FALSE)
|
|
{
|
|
$sql = 'DELETE ';
|
|
|
|
$sql .= $with_priority ? $query->getPriority() : '';
|
|
$tables = $query->getTables();
|
|
|
|
$sql .= $tables[0]->getAlias();
|
|
|
|
$from = $query->getFromString($with_values);
|
|
if($from == '')
|
|
{
|
|
return new Object(-1, "Invalid query");
|
|
}
|
|
$sql .= ' FROM ' . $from;
|
|
|
|
$where = $query->getWhereString($with_values);
|
|
if($where != '')
|
|
{
|
|
$sql .= ' WHERE ' . $where;
|
|
}
|
|
|
|
return $sql;
|
|
}
|
|
|
|
/**
|
|
* Return update query string
|
|
* @param object $query
|
|
* @param boolean $with_values
|
|
* @param boolean $with_priority
|
|
* @return string
|
|
*/
|
|
function getUpdateSql($query, $with_values = TRUE, $with_priority = FALSE)
|
|
{
|
|
$columnsList = $query->getUpdateString($with_values);
|
|
if($columnsList == '')
|
|
{
|
|
return new Object(-1, "Invalid query");
|
|
}
|
|
|
|
$tables = $query->getFromString($with_values);
|
|
if($tables == '')
|
|
{
|
|
return new Object(-1, "Invalid query");
|
|
}
|
|
|
|
$where = $query->getWhereString($with_values);
|
|
if($where != '')
|
|
{
|
|
$where = ' WHERE ' . $where;
|
|
}
|
|
|
|
$priority = $with_priority ? $query->getPriority() : '';
|
|
|
|
return "UPDATE $priority $tables SET $columnsList " . $where;
|
|
}
|
|
|
|
/**
|
|
* Return insert query string
|
|
* @param object $query
|
|
* @param boolean $with_values
|
|
* @param boolean $with_priority
|
|
* @return string
|
|
*/
|
|
function getInsertSql($query, $with_values = TRUE, $with_priority = FALSE)
|
|
{
|
|
$tableName = $query->getFirstTableName();
|
|
$values = $query->getInsertString($with_values);
|
|
$priority = $with_priority ? $query->getPriority() : '';
|
|
|
|
return "INSERT $priority INTO $tableName \n $values";
|
|
}
|
|
|
|
/**
|
|
* Return index from slave server list
|
|
* @return int
|
|
*/
|
|
function _getSlaveConnectionStringIndex()
|
|
{
|
|
$max = count($this->slave_db);
|
|
$indx = rand(0, $max - 1);
|
|
return $indx;
|
|
}
|
|
|
|
/**
|
|
* Return connection resource
|
|
* @param string $type use 'master' or 'slave'. default value is 'master'
|
|
* @param int $indx if indx value is NULL, return rand number in slave server list
|
|
* @return resource
|
|
*/
|
|
function _getConnection($type = 'master', $indx = NULL)
|
|
{
|
|
if($type == 'master')
|
|
{
|
|
if(!$this->master_db['is_connected'])
|
|
{
|
|
$this->_connect($type);
|
|
}
|
|
$this->connection = 'Master ' . $this->master_db['db_hostname'];
|
|
return $this->master_db["resource"];
|
|
}
|
|
|
|
if($indx === NULL)
|
|
{
|
|
$indx = $this->_getSlaveConnectionStringIndex($type);
|
|
}
|
|
|
|
if(!$this->slave_db[$indx]['is_connected'])
|
|
{
|
|
$this->_connect($type, $indx);
|
|
}
|
|
|
|
$this->connection = 'Slave ' . $this->slave_db[$indx]['db_hostname'];
|
|
return $this->slave_db[$indx]["resource"];
|
|
}
|
|
|
|
/**
|
|
* check db information exists
|
|
* @return boolean
|
|
*/
|
|
function _dbInfoExists()
|
|
{
|
|
if(!$this->master_db)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if(count($this->slave_db) === 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* DB disconnection
|
|
* this method is protected
|
|
* @param resource $connection
|
|
* @return void
|
|
*/
|
|
function _close($connection)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* DB disconnection
|
|
* @param string $type 'master' or 'slave'
|
|
* @param int $indx number in slave dbms server list
|
|
* @return void
|
|
*/
|
|
function close($type = 'master', $indx = 0)
|
|
{
|
|
if(!$this->isConnected($type, $indx))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if($type == 'master')
|
|
{
|
|
$connection = &$this->master_db;
|
|
}
|
|
else
|
|
{
|
|
$connection = &$this->slave_db[$indx];
|
|
}
|
|
|
|
$this->commit();
|
|
$this->_close($connection["resource"]);
|
|
|
|
$connection["is_connected"] = FALSE;
|
|
}
|
|
|
|
/**
|
|
* DB transaction start
|
|
* this method is protected
|
|
* @return boolean
|
|
*/
|
|
function _begin()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* DB transaction start
|
|
* @return void
|
|
*/
|
|
function begin()
|
|
{
|
|
if(!$this->isConnected())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if($this->_begin($this->transactionNestedLevel))
|
|
{
|
|
$this->transaction_started = TRUE;
|
|
$this->transactionNestedLevel++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DB transaction rollback
|
|
* this method is protected
|
|
* @return boolean
|
|
*/
|
|
function _rollback()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* DB transaction rollback
|
|
* @return void
|
|
*/
|
|
function rollback()
|
|
{
|
|
if(!$this->isConnected() || !$this->transaction_started)
|
|
{
|
|
return;
|
|
}
|
|
if($this->_rollback($this->transactionNestedLevel))
|
|
{
|
|
$this->transactionNestedLevel--;
|
|
|
|
if(!$this->transactionNestedLevel)
|
|
{
|
|
$this->transaction_started = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DB transaction commit
|
|
* this method is protected
|
|
* @return boolean
|
|
*/
|
|
function _commit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* DB transaction commit
|
|
* @param boolean $force regardless transaction start status or connect status, forced to commit
|
|
* @return void
|
|
*/
|
|
function commit($force = FALSE)
|
|
{
|
|
if(!$force && (!$this->isConnected() || !$this->transaction_started))
|
|
{
|
|
return;
|
|
}
|
|
if($this->transactionNestedLevel == 1 && $this->_commit())
|
|
{
|
|
$this->transaction_started = FALSE;
|
|
$this->transactionNestedLevel = 0;
|
|
}
|
|
else
|
|
{
|
|
$this->transactionNestedLevel--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute the query
|
|
* this method is protected
|
|
* @param string $query
|
|
* @param resource $connection
|
|
* @return void
|
|
*/
|
|
function __query($query, $connection)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Execute the query
|
|
* this method is protected
|
|
* @param string $query
|
|
* @param resource $connection
|
|
* @return resource
|
|
*/
|
|
function _query($query, $connection = NULL)
|
|
{
|
|
if($connection == NULL)
|
|
{
|
|
$connection = $this->_getConnection('master');
|
|
}
|
|
// Notify to start a query execution
|
|
$this->actStart($query);
|
|
|
|
// Run the query statement
|
|
$result = $this->__query($query, $connection);
|
|
|
|
// Notify to complete a query execution
|
|
$this->actFinish();
|
|
// Return result
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* DB info settings
|
|
* this method is protected
|
|
* @return void
|
|
*/
|
|
function _setDBInfo()
|
|
{
|
|
$db_info = Context::getDBInfo();
|
|
$this->master_db = $db_info->master_db;
|
|
if($db_info->master_db["db_hostname"] == $db_info->slave_db[0]["db_hostname"]
|
|
&& $db_info->master_db["db_port"] == $db_info->slave_db[0]["db_port"]
|
|
&& $db_info->master_db["db_userid"] == $db_info->slave_db[0]["db_userid"]
|
|
&& $db_info->master_db["db_password"] == $db_info->slave_db[0]["db_password"]
|
|
&& $db_info->master_db["db_database"] == $db_info->slave_db[0]["db_database"]
|
|
)
|
|
{
|
|
$this->slave_db[0] = &$this->master_db;
|
|
}
|
|
else
|
|
{
|
|
$this->slave_db = $db_info->slave_db;
|
|
}
|
|
$this->prefix = $db_info->master_db["db_table_prefix"];
|
|
$this->use_prepared_statements = $db_info->use_prepared_statements;
|
|
}
|
|
|
|
/**
|
|
* DB Connect
|
|
* this method is protected
|
|
* @param array $connection
|
|
* @return void
|
|
*/
|
|
function __connect($connection)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* If have a task after connection, add a taks in this method
|
|
* this method is protected
|
|
* @param resource $connection
|
|
* @return void
|
|
*/
|
|
function _afterConnect($connection)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* DB Connect
|
|
* this method is protected
|
|
* @param string $type 'master' or 'slave'
|
|
* @param int $indx number in slave dbms server list
|
|
* @return void
|
|
*/
|
|
function _connect($type = 'master', $indx = 0)
|
|
{
|
|
if($this->isConnected($type, $indx))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ignore if no DB information exists
|
|
if(!$this->_dbInfoExists())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if($type == 'master')
|
|
{
|
|
$connection = &$this->master_db;
|
|
}
|
|
else
|
|
{
|
|
$connection = &$this->slave_db[$indx];
|
|
}
|
|
|
|
$result = $this->__connect($connection);
|
|
if($result === NULL || $result === FALSE)
|
|
{
|
|
$connection["is_connected"] = FALSE;
|
|
return;
|
|
}
|
|
|
|
// Check connections
|
|
$connection["resource"] = $result;
|
|
$connection["is_connected"] = TRUE;
|
|
|
|
// Save connection info for db logs
|
|
$this->connection = ucfirst($type) . ' ' . $connection["db_hostname"];
|
|
|
|
// regist $this->close callback
|
|
register_shutdown_function(array($this, "close"));
|
|
|
|
$this->_afterConnect($result);
|
|
}
|
|
|
|
/**
|
|
* Start recording DBClass log
|
|
* @return void
|
|
*/
|
|
function actDBClassStart()
|
|
{
|
|
$this->setError(0, 'success');
|
|
$this->act_dbclass_start = getMicroTime();
|
|
$this->elapsed_dbclass_time = 0;
|
|
}
|
|
|
|
/**
|
|
* Finish recording DBClass log
|
|
* @return void
|
|
*/
|
|
function actDBClassFinish()
|
|
{
|
|
if(!$this->query)
|
|
{
|
|
return;
|
|
}
|
|
$this->act_dbclass_finish = getMicroTime();
|
|
$elapsed_dbclass_time = $this->act_dbclass_finish - $this->act_dbclass_start;
|
|
$this->elapsed_dbclass_time = $elapsed_dbclass_time;
|
|
$GLOBALS['__dbclass_elapsed_time__'] += $elapsed_dbclass_time;
|
|
}
|
|
|
|
/**
|
|
* Returns a database specific parser instance
|
|
* used for escaping expressions and table/column identifiers
|
|
*
|
|
* Requires an implementation of the DB class (won't work if database is not set)
|
|
* this method is singleton
|
|
*
|
|
* @param boolean $force force load DBParser instance
|
|
* @return DBParser
|
|
*/
|
|
function &getParser($force = FALSE)
|
|
{
|
|
static $dbParser = NULL;
|
|
if(!$dbParser || $force)
|
|
{
|
|
$oDB = DB::getInstance();
|
|
$dbParser = $oDB->getParser();
|
|
}
|
|
|
|
return $dbParser;
|
|
}
|
|
|
|
}
|
|
/* End of file DB.class.php */
|
|
/* Location: ./classes/db/DB.class.php */
|