diff --git a/config/config.php b/config/config.php index 6f803d1f0b..5f7b22eb9d 100644 --- a/config/config.php +++ b/config/config.php @@ -68,6 +68,9 @@ $config->db->strictMode = false; // 关闭MySQL的严格模式。 Turn $config->db->prefix = 'zt_'; // 数据库表名前缀。 The prefix of the table name. $config->slaveDBList = array(); // 支持多个从库。 Support multiple slave dbs. +$config->metricDB = new stdclass(); +$config->metricDB->type = 'mysql'; // 度量计算数据库类型。 The type of metric database. + /* 可用域名后缀列表。Domain postfix lists. */ $config->domainPostfix = "|com|com.cn|com.hk|com.tw|com.vc|edu.cn|es|"; $config->domainPostfix .= "|eu|fm|gov.cn|gs|hk|im|in|info|jp|kr|la|me|"; diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 194ca63565..238f9fa204 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -417,6 +417,7 @@ class baseRouter $this->loadClass('filter', $static = true); $this->loadClass('form', $static = true); $this->loadClass('dbh', $static = true); + $this->loadClass('sqlite', $static = true); $this->loadClass('dao', $static = true); $this->loadClass('mobile', $static = true); @@ -2929,6 +2930,25 @@ class baseRouter } } + /** + * 连接SQLite数据库。 + * Connect SQLite database. + * + * @param object $params + * @access public + * @return object + */ + public function connectSqlite($params = null) + { + if(empty($params)) + { + $params = new stdclass(); + $params->file = $this->getTmpRoot() . 'sqlite.db'; + } + + return new sqlite($params); + } + /** * Query from database, use master or slave db. * diff --git a/lib/sqlite/sqlite.class.php b/lib/sqlite/sqlite.class.php new file mode 100644 index 0000000000..ecfe80cd67 --- /dev/null +++ b/lib/sqlite/sqlite.class.php @@ -0,0 +1,224 @@ + + * @package sqlite + * @link http://www.zentao.net + */ +class sqlite +{ + /** + * 全局$app对象。 + * The global app object. + * + * @var object + * @access public + */ + public $app; + + /** + * 全局$config对象。 + * The global config object. + * + * @var object + * @access public + */ + public $config; + + /** + * 全局$mysql对象。 + * The global mysql object. + * + * @var object + * @access public + */ + public $mysql; + + /** + * 全局$sqlite对象。 + * The global sqlite object. + * + * @var object + * @access public + */ + public $dbh = null; + + /** + * __construct. + * + * @access public + * @return void + */ + public function __construct(object $params) + { + global $app, $config, $dbh; + $this->app = $app; + $this->mysql = $dbh; + $this->config = $config; + + $file = empty($params) || !isset($params->file) ? '' : $params->file; + $this->connectSqlite($file); + } + + /** + * 使用PDO连接SQLite数据库。 + * Connect to sqlite database by PDO. + * + * @param string $sqliteFile + * @access public + * @return object + */ + public function connectSqlite(string $sqliteFile = ''): object + { + $tmpRoot = $this->app->getTmpRoot(); + if(empty($sqliteFile) || !is_file($sqliteFile)) $sqliteFile = $tmpRoot . 'sqlite.db'; + + $sqliteFile = realpath($sqliteFile); + if(strpos($sqliteFile, $tmpRoot) !== 0) return helper::end("The sqlite file '$sqliteFile' is not in the tmp root '$tmpRoot'"); + if(!is_file($sqliteFile)) return helper::end("The sqlite file '$sqliteFile' is not exists"); + + $dbh = new PDO("sqlite:$sqliteFile"); + $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $this->dbh = $dbh; + + return $this; + } + + /** + * 将MySQL数据类型转化为SQLite数据类型。 + * Convert MySQL attr to SQLite attr. + * + * @param string $sql + * @access public + * @return string + */ + public function formatAttr(string $sql): string + { + $sql = str_replace('`', '', $sql); + $sql = preg_replace('/\s*int\s*\(\d+\)\s*NOT NULL AUTO_INCREMENT/i', ' INTEGER PRIMARY KEY AUTOINCREMENT', $sql); + $sql = preg_replace('/\s*int\s*\(\d+\)\s*AUTO_INCREMENT/i', ' INTEGER PRIMARY KEY AUTOINCREMENT', $sql); + $sql = preg_replace('/\s*tinyint\(\d+\)/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*smallint\(\d+\)/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*mediumint\(\d+\)/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*int\(\d+\)/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*bigint\(\d+\)/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*float/i', ' REAL', $sql); + $sql = preg_replace('/\s*double/i', ' REAL', $sql); + $sql = preg_replace('/\s*decimal/i', ' REAL', $sql); + $sql = preg_replace('/\s*datetime/i', ' TEXT', $sql); + $sql = preg_replace('/\s*timestamp/i', ' TEXT', $sql); + $sql = preg_replace('/\s*time/i', ' TEXT', $sql); + $sql = preg_replace('/\s*date/i', ' TEXT', $sql); + $sql = preg_replace('/\s*enum\([^)]*\)/i', ' TEXT', $sql); + $sql = preg_replace('/\s*set\([^)]*\)/i', ' TEXT', $sql); + $sql = preg_replace('/\s*year/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*bit/i', ' INTEGER', $sql); + $sql = preg_replace('/\s*UNSIGNED/i', '', $sql); + $sql = preg_replace('/\s*DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP/i', '', $sql); + + return $sql; + } + + /** + * 处理SQL语句。 + * Format sql. + * + * @param string $sql + * @access public + * @return string + */ + public function formatSQL(string $sql): string + { + // $sql = $this->formatAttr($sql); + return $sql; + } + + /** + * 尝试从SQLite中执行查询语句,如果抛出异常,则从MySQL中执行查询语句。 + * Query sql by SQLite, if throw exception, query sql by MySQL. + * + * @param string $sql + * @access public + * @return PDOStatement|false + */ + public function query(string $sql): PDOStatement|false + { + try + { + return $this->dbh->query($this->formatSQL($sql)); + } + catch(PDOException $e) + { + return $this->mysql->query($sql); + } + } + + /** + * 执行查询语句。 + * Query sql without format. + * + * @param string $sql + * @access public + * @return PDOStatement|false + */ + public function rawQuery($sql): PDOStatement|false + { + $stmt = $this->dbh->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * 执行SQL语句,返回受影响的行数。 + * Execute sql. + * + * @param string $sql + * @access public + * @return int|false + */ + public function exec(string $sql): int|false + { + return $this->dbh->exec($sql); + } + + /** + * 开始事务。 + * Begin transaction. + * + * @access public + * @return bool + */ + public function beginTransaction(): bool + { + return $this->dbh->beginTransaction(); + } + + /** + * 回滚事务。 + * Roll back if transaction failed. + * + * @access public + * @return bool + */ + public function rollBack(): bool + { + return $this->dbh->rollBack(); + } + + /** + * 提交事务。 + * Commit transaction. + * + * @access public + * @return bool + */ + public function commit(): bool + { + return $this->dbh->commit(); + } +} diff --git a/module/metric/model.php b/module/metric/model.php index 4cce26b098..72be15a9d2 100755 --- a/module/metric/model.php +++ b/module/metric/model.php @@ -488,6 +488,28 @@ class metricModel extends model return $measurement; } + /** + * 获取dao对象。 + * Get dao object. + * + * @access public + * @return object + */ + public function getDAO() + { + if($this->config->metricDB->type == 'sqlite') + { + $dao = clone $this->dao; + $dao->reset(); + + $dao->dbh = $this->app->connectSqlite(); + $dao->driver = 'sqlite'; + + return $dao; + } + return $this->dao; + } + /** * 获取度量项数据源句柄。 * Get data source statement of calculator. @@ -499,11 +521,12 @@ class metricModel extends model */ public function getDataStatement($calculator, $returnType = 'statement') { + $dao = $this->getDAO(); if(!empty($calculator->dataset)) { include_once $this->getDatasetPath(); - $dataset = new dataset($this->dao); + $dataset = new dataset($dao); $dataSource = $calculator->dataset; $fieldList = implode(',', $calculator->fieldList); @@ -512,7 +535,7 @@ class metricModel extends model } else { - $calculator->setDAO($this->dao); + $calculator->setDAO($dao); $scm = $this->app->loadClass('scm'); $calculator->setSCM($scm); @@ -829,11 +852,11 @@ class metricModel extends model * @access public * @return dataset */ - public function getDataset() + public function getDataset($dao) { $datasetPath = $this->getDatasetPath(); include_once $datasetPath; - return new dataset($this->dao); + return new dataset($dao); } /** @@ -884,13 +907,19 @@ class metricModel extends model $aliasList = array(); foreach($uniqueList as $field) { - if(strpos($field, '.') === false || strpos(strtoupper($field), ' AS ') !== false) + if(strpos(strtoupper($field), ' AS ') !== false) { $aliasList[] = $field; continue; } - $alias = str_replace('.', '_', $field); - $aliasList[] = $field . ' AS ' . $alias; + if(strpos($field, '.') !== false) + { + $alias = str_replace('.', '_', $field); + list($table, $field) = explode('.', $field); + $aliasList[] = "`$table`.`$field` AS `$alias`"; + continue; + } + $aliasList[] = "`$field`"; } return implode(',', $aliasList); } diff --git a/module/metric/zen.php b/module/metric/zen.php index f788e07678..829b33f88e 100644 --- a/module/metric/zen.php +++ b/module/metric/zen.php @@ -86,18 +86,20 @@ class metricZen extends metric */ protected function prepareDataset($calcGroup) { + $dao = $this->metric->getDAO(); + $dataSource = $calcGroup->dataset; $calcList = $calcGroup->calcList; if(empty($dataSource)) { $calc = current($calcList); - $calc->setDAO($this->dao); + $calc->setDAO($dao); return $calc->getStatement(); } - $dataset = $this->metric->getDataset($this->dao); + $dataset = $this->metric->getDataset($dao); $fieldList = $this->metric->uniteFieldList($calcList); return $dataset->$dataSource($fieldList);