From 59f14d8a3f9b79ade32e9bbd46e38eafa71ec27b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 22 Jun 2020 11:00:18 +0900 Subject: [PATCH 01/47] Import DB parser classes from previous private project --- common/framework/parsers/dbqueryparser.php | 27 ++++ common/framework/parsers/dbtable/column.php | 18 +++ .../framework/parsers/dbtable/constraint.php | 15 ++ common/framework/parsers/dbtable/index.php | 13 ++ common/framework/parsers/dbtable/table.php | 15 ++ common/framework/parsers/dbtableparser.php | 143 ++++++++++++++++++ 6 files changed, 231 insertions(+) create mode 100644 common/framework/parsers/dbqueryparser.php create mode 100644 common/framework/parsers/dbtable/column.php create mode 100644 common/framework/parsers/dbtable/constraint.php create mode 100644 common/framework/parsers/dbtable/index.php create mode 100644 common/framework/parsers/dbtable/table.php create mode 100644 common/framework/parsers/dbtableparser.php diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php new file mode 100644 index 000000000..7958e2376 --- /dev/null +++ b/common/framework/parsers/dbqueryparser.php @@ -0,0 +1,27 @@ +name = preg_replace('/\.xml$/', '', basename($filename)); + + // Load the XML file. + $xml = simplexml_load_file($filename); + if ($xml === false) + { + return false; + } + + // Load columns. + foreach ($xml->column as $column_info) + { + // Get the column name and type. + $column = new DBTable\Column; + $column->name = strval($column_info['name']); + $column->type = strval($column_info['type']); + + // Get the size. + if (preg_match('/^([a-z0-9_]+)\(([0-9,\s]+)\)$/i', $column->type, $matches)) + { + $column->type = $matches[1]; + $column->size = $matches[2]; + } + if (isset($column_info['size'])) + { + $column->size = strval($column_info['size']); + } + $column->size = implode(',', array_map('trim', explode(',', $column->size))) ?: null; + + // Get the utf8mb4 attribute. + if (isset($column_info['utf8mb4'])) + { + $column->utf8mb4 = toBool(strval($column_info['utf8mb4'])); + } + + // Get the default value. + if (isset($column_info['default'])) + { + $column->default_value = strval($column_info['default']); + } + + // Get the NOT NULL attribute. + if (isset($column_info['notnull']) || isset($column_info['not-null'])) + { + $attr = strval($column_info['notnull'] ?: $column_info['not-null']); + $column->not_null = ($attr === 'notnull' || $attr === 'not-null' || toBool($attr)); + } + + // Get index information. + if (isset($column_info['index'])) + { + $index_name = strval($column_info['index']); + if (!isset($table->indexes[$index_name])) + { + $table->indexes[$index_name] = new DBTable\Index; + $table->indexes[$index_name]->name = $index_name; + } + $table->indexes[$index_name]->columns[] = $column->name; + } + if (isset($column_info['unique'])) + { + $index_name = strval($column_info['unique']); + if (!isset($table->indexes[$index_name])) + { + $table->indexes[$index_name] = new DBTable\Index; + $table->indexes[$index_name]->name = $index_name; + $table->indexes[$index_name]->is_unique = true; + } + $table->indexes[$index_name]->columns[] = $column->name; + } + + // Get primary key information. + if (isset($column_info['primary_key']) || isset($column_info['primary-key'])) + { + $attr = strval($column_info['primary_key'] ?: $column_info['primary-key']); + if ($attr === 'primary_key' || $attr === 'primary-key' || toBool($attr)) + { + $table->primary_key[] = $column->name; + $column->is_primary_key = true; + } + } + + // Get auto-increment information. + if (isset($column_info['auto_increment']) || isset($column_info['auto-increment'])) + { + $attr = strval($column_info['auto_increment'] ?: $column_info['auto-increment']); + if ($attr === 'auto_increment' || $attr === 'auto-increment' || toBool($attr)) + { + $column->auto_increment = true; + } + } + + // Add the column to the table definition. + $table->columns[$column->name] = $column; + } + + // Load indexes. + foreach ($xml->index as $index_info) + { + $index = new DBTable\Index; + $index->name = strval($index_info['name']); + $index->columns = array_map('trim', explode(',', strval($index_info['columns']))); + $index->is_unique = ($index_info['unique'] === 'unique' || toBool(strval($index_info['unique']))); + $table->indexes[$index->name] = $index; + } + + // Load other constraints (foreign keys). + foreach ($xml->constraint as $const_info) + { + $const = new DBTable\Constraint; + $const->type = strtolower($const_info['type']); + $const->column = strval($const_info['column']); + $const->references = strval($const_info['references']); + $const->on_update = strtolower($const_info['on_update'] ?: $const_info['on-update']); + $const->on_delete = strtolower($const_info['on_delete'] ?: $const_info['on-delete']); + $table->constraints[] = $const; + } + + // Return the complete table definition. + return $table; + } +} From 43c5da7818712a5d0722325cd76feb3307d0486e Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 22 Jun 2020 11:19:26 +0900 Subject: [PATCH 02/47] Update parser classes --- common/framework/parsers/dbqueryparser.php | 4 ++-- common/framework/parsers/dbtable/column.php | 1 + common/framework/parsers/dbtableparser.php | 25 +++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index 7958e2376..eb37dc680 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -15,10 +15,10 @@ class DBQueryParser * @param string $filename * @return object|false */ - public static function loadXML($filename) + public static function loadXML(string $filename) { // Load the XML file. - $xml = simplexml_load_file($filename); + $xml = simplexml_load_string(file_get_contents($filename)); if ($xml === false) { return false; diff --git a/common/framework/parsers/dbtable/column.php b/common/framework/parsers/dbtable/column.php index 684b37a03..119c9b0a2 100644 --- a/common/framework/parsers/dbtable/column.php +++ b/common/framework/parsers/dbtable/column.php @@ -9,6 +9,7 @@ class Column { public $name; public $type; + public $xetype; public $size; public $utf8mb4 = true; public $default_value; diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index 3ca556c01..d3d822861 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -9,20 +9,30 @@ use Rhymix\Framework\Storage; */ class DBTableParser { + /** + * Mapping for XE-compatible types. + */ + protected static $_xe_types = array( + 'bignumber' => 'bigint', + 'number' => 'bigint', + 'bigtext' => 'longtext', + 'date' => 'char(14)', + ); + /** * Load a table definition XML file. * * @param string $filename * @return object|false */ - public static function loadXML($filename) + public static function loadXML(string $filename) { // Initialize table definition. $table = new DBTable\Table; $table->name = preg_replace('/\.xml$/', '', basename($filename)); // Load the XML file. - $xml = simplexml_load_file($filename); + $xml = simplexml_load_string(file_get_contents($filename)); if ($xml === false) { return false; @@ -36,6 +46,17 @@ class DBTableParser $column->name = strval($column_info['name']); $column->type = strval($column_info['type']); + // Map XE-compatible types to database native types. + if (isset(self::$_xe_types[$column->type])) + { + $column->xetype = $column->type; + $column->type = self::$_xe_types[$column->type]; + } + else + { + $column->xetype = $column->type; + } + // Get the size. if (preg_match('/^([a-z0-9_]+)\(([0-9,\s]+)\)$/i', $column->type, $matches)) { From c97b161e4208f24e6d31057b72712aff07d668bb Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 22 Jun 2020 11:26:11 +0900 Subject: [PATCH 03/47] Remove unnecessary size from int/bigint types --- common/framework/parsers/dbtableparser.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index d3d822861..5078357e2 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -19,6 +19,14 @@ class DBTableParser 'date' => 'char(14)', ); + /** + * List of types for which the size attribute will be ignored. + */ + protected static $_nosize_types = array( + 'bigint' => true, + 'int' => true, + ); + /** * Load a table definition XML file. * @@ -68,6 +76,10 @@ class DBTableParser $column->size = strval($column_info['size']); } $column->size = implode(',', array_map('trim', explode(',', $column->size))) ?: null; + if (isset(self::$_nosize_types[$column->type])) + { + $column->size = null; + } // Get the utf8mb4 attribute. if (isset($column_info['utf8mb4'])) From 20025077f70e6037b64c5d559ba836ddbbdd35f3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 22 Jun 2020 16:53:44 +0900 Subject: [PATCH 04/47] Create basic structure of XML query --- common/framework/parsers/dbquery/column.php | 14 ++ .../framework/parsers/dbquery/condition.php | 16 ++ .../parsers/dbquery/conditiongroup.php | 12 ++ .../framework/parsers/dbquery/genericvar.php | 12 ++ common/framework/parsers/dbquery/groupby.php | 12 ++ .../framework/parsers/dbquery/navigation.php | 15 ++ common/framework/parsers/dbquery/orderby.php | 14 ++ common/framework/parsers/dbquery/query.php | 18 ++ common/framework/parsers/dbquery/table.php | 14 ++ common/framework/parsers/dbqueryparser.php | 174 +++++++++++++++++- common/framework/parsers/dbtableparser.php | 10 +- 11 files changed, 303 insertions(+), 8 deletions(-) create mode 100644 common/framework/parsers/dbquery/column.php create mode 100644 common/framework/parsers/dbquery/condition.php create mode 100644 common/framework/parsers/dbquery/conditiongroup.php create mode 100644 common/framework/parsers/dbquery/genericvar.php create mode 100644 common/framework/parsers/dbquery/groupby.php create mode 100644 common/framework/parsers/dbquery/navigation.php create mode 100644 common/framework/parsers/dbquery/orderby.php create mode 100644 common/framework/parsers/dbquery/query.php create mode 100644 common/framework/parsers/dbquery/table.php diff --git a/common/framework/parsers/dbquery/column.php b/common/framework/parsers/dbquery/column.php new file mode 100644 index 000000000..c6eaea342 --- /dev/null +++ b/common/framework/parsers/dbquery/column.php @@ -0,0 +1,14 @@ +name = $name ?: null; + $query->alias = trim($xml['alias']) ?: null; + if ($query->alias && !$query->name) + { + $query->name = $query->alias; + } + $query->type = trim($xml['action']); + + // Load tables. + foreach ($xml->tables->table as $tag) + { + if (trim($tag['query']) === 'true') + { + $table = self::_parseQuery($tag); + $query->tables[$table->alias] = $table; + } + else + { + $table = new DBQuery\Table; + $table->name = trim($tag['name']); + $table->alias = trim($tag['alias']) ?: $table->name; + $table_type = trim($tag['type']); + if (stripos($table_type, 'join') !== false) + { + $table->join_type = strtoupper($table_type); + if ($tag->conditions) + { + $table->join_conditions = self::_parseConditions($tag->conditions); + } + } + $query->tables[$table->alias] = $table; + } + } + + // Load columns. + foreach ($xml->columns->column as $tag) + { + $column = new DBQuery\Column; + $column->name = trim($tag['name']); + $column->alias = trim($tag['alias']) ?: null; + if ($column->name === '*' || preg_match('/\.\*$/', $column->name)) + { + $column->is_wildcard = true; + } + if (!self::_isValidColumnName($column->name)) + { + $column->is_expression = true; + } + $query->columns[] = $column; + } + + // Load conditions. + if ($xml->conditions) + { + $query->conditions = self::_parseConditions($xml->conditions); + } + + // Load groups. + if ($xml->groups) + { + $query->groupby = new DBQuery\GroupBy; + foreach ($xml->groups->children() as $tag) + { + $name = $tag->getName(); + if ($name === 'group') + { + $query->groupby->columns[] = trim($tag['column']); + } + elseif ($name === 'having') + { + $query->groupby->having = self::_parseConditions($tag); + } + } + } + + // Load navigation settings. + if ($xml->navigation) + { + $query->navigation = new DBQuery\Navigation; + foreach ($xml->navigation->index as $tag) + { + $orderby = new DBQuery\OrderBy; + $orderby->var = trim($tag['var']) ?: null; + $orderby->default = trim($tag['default']) ?: null; + $orderby->order_var = trim($tag['order']) ?: null; + $query->navigation->orderby[] = $orderby; + } + foreach (['list_count', 'page_count', 'page', 'offset'] as $key) + { + if ($tag = $xml->navigation->{$key}) + { + $query->navigation->{$key} = new DBQuery\GenericVar; + $query->navigation->{$key}->var = trim($tag['var']) ?: null; + $query->navigation->{$key}->default = trim($tag['default']) ?: null; + } + } + } + + // Return the complete query definition. + return $query; + } + + /** + * Parse conditions. + * + * @param SimpleXMLElement $parent + * @return array + */ + protected static function _parseConditions(\SimpleXMLElement $parent): array + { + $result = array(); + foreach ($parent->children() as $tag) + { + $name = $tag->getName(); + if ($name === 'condition') + { + $cond = new DBQuery\Condition; + $cond->operation = trim($tag['operation']); + $cond->column = trim($tag['column']); + $cond->var = trim($tag['var']); + $cond->default = trim($tag['default']); + $cond->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; + $cond->operator = strtoupper($tag['pipe']) ?: 'AND'; + $result[] = $cond; + } + elseif ($name === 'group') + { + $group = new DBQuery\ConditionGroup; + $group->conditions = self::_parseConditions($tag); + $group->operator = strtoupper($tag['pipe']) ?: 'AND'; + $result[] = $group; + } + } + + return $result; + } + + /** + * Check if an expression might be a valid column name. + * + * @param string $column_name + * @return bool + */ + protected static function _isValidColumnName(string $column_name): bool + { + if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/', $column_name)) + { + return true; + } + else + { + return false; + } } } diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index 5078357e2..3fda78870 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -2,8 +2,6 @@ namespace Rhymix\Framework\Parsers; -use Rhymix\Framework\Storage; - /** * DB table parser class for XE compatibility. */ @@ -35,10 +33,6 @@ class DBTableParser */ public static function loadXML(string $filename) { - // Initialize table definition. - $table = new DBTable\Table; - $table->name = preg_replace('/\.xml$/', '', basename($filename)); - // Load the XML file. $xml = simplexml_load_string(file_get_contents($filename)); if ($xml === false) @@ -46,6 +40,10 @@ class DBTableParser return false; } + // Initialize table definition. + $table = new DBTable\Table; + $table->name = preg_replace('/\.xml$/', '', basename($filename)); + // Load columns. foreach ($xml->column as $column_info) { From b912c623e22cb39b28dddb1852ec8184bf1154b1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 22 Jun 2020 23:43:56 +0900 Subject: [PATCH 05/47] More work on the query parser --- .../framework/parsers/dbquery/condition.php | 7 +- .../parsers/dbquery/conditiongroup.php | 2 +- .../framework/parsers/dbquery/expression.php | 49 ++++++++++++ .../framework/parsers/dbquery/genericvar.php | 12 --- common/framework/parsers/dbquery/orderby.php | 2 +- common/framework/parsers/dbquery/query.php | 3 + .../parsers/dbquery/variablebase.php | 76 +++++++++++++++++++ common/framework/parsers/dbqueryparser.php | 66 +++++++++++----- 8 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 common/framework/parsers/dbquery/expression.php delete mode 100644 common/framework/parsers/dbquery/genericvar.php create mode 100644 common/framework/parsers/dbquery/variablebase.php diff --git a/common/framework/parsers/dbquery/condition.php b/common/framework/parsers/dbquery/condition.php index b386fb894..cb5ecf690 100644 --- a/common/framework/parsers/dbquery/condition.php +++ b/common/framework/parsers/dbquery/condition.php @@ -5,12 +5,15 @@ namespace Rhymix\Framework\Parsers\DBQuery; /** * Condition class. */ -class Condition extends GenericVar +class Condition extends VariableBase { public $operation; public $column; public $var; public $default; public $not_null; - public $operator = 'AND'; + public $filter; + public $minlength = 0; + public $maxlength = 0; + public $pipe = 'AND'; } diff --git a/common/framework/parsers/dbquery/conditiongroup.php b/common/framework/parsers/dbquery/conditiongroup.php index caee07f1d..a5e67ce4d 100644 --- a/common/framework/parsers/dbquery/conditiongroup.php +++ b/common/framework/parsers/dbquery/conditiongroup.php @@ -8,5 +8,5 @@ namespace Rhymix\Framework\Parsers\DBQuery; class ConditionGroup { public $conditions = array(); - public $operator = 'AND'; + public $pipe = 'AND'; } diff --git a/common/framework/parsers/dbquery/expression.php b/common/framework/parsers/dbquery/expression.php new file mode 100644 index 000000000..5ad98717c --- /dev/null +++ b/common/framework/parsers/dbquery/expression.php @@ -0,0 +1,49 @@ +type = $type; + $this->value = $value; + } + + /** + * Return the string representation of this expression. + * + * @return string + */ + public function __toString() + { + switch ($this->type) + { + case 'var': + return ':' . $this->value; + case 'null': + return 'NULL'; + case 'string': + return; + case 'int': + return $this->value; + + + } + } +} diff --git a/common/framework/parsers/dbquery/genericvar.php b/common/framework/parsers/dbquery/genericvar.php deleted file mode 100644 index ca6064a13..000000000 --- a/common/framework/parsers/dbquery/genericvar.php +++ /dev/null @@ -1,12 +0,0 @@ -var) + { + return new Expression('var', $this->var); + } + else + { + return $this->getDefaultValue(); + } + } + + /** + * Get the default value of this variable. + * + * @return Expression + */ + public function getDefaultValue(): Expression + { + // If the default value is not set, return null. + $val = $this->default; + if ($val === null) + { + return new Expression('null'); + } + + // If the default value is a function shortcut, return an appropriate value. + switch ($val) + { + case 'ipaddress()': + return new Expression('string', \RX_CLIENT_IP); + case 'unixtime()': + return new Expression('string', time()); + case 'curdate()': + case 'date()': + return new Expression('string', date('YmdHis')); + case 'sequence()': + return new Expression('int', getNextSequence()); + } + + // If the default value is a calculation based on the current value, return a query string. + if (isset($this->column) && preg_match('/^(plus|minus|multiply)\(([0-9]+)\)$/', $val, $matches)) + { + + } + + // If the default value is a column name, return the column name.20 + if (\Rhymix\Framework\Parsers\DBQueryParser::isValidColumnName($val)) + { + + } + + // Otherwise, return the literal value. + return new Expression('string', $val); + } +} diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index a0fa28e08..d4cd3a28f 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -25,7 +25,6 @@ class DBQueryParser // Parse the query. $query_name = preg_replace('/\.xml$/', '', basename($filename)); $query = self::_parseQuery($xml, $query_name); - return $query; } @@ -46,15 +45,20 @@ class DBQueryParser { $query->name = $query->alias; } - $query->type = trim($xml['action']); + $query->type = strtoupper($xml['action']) ?: null; + + // Load attributes that only apply to subqueries in the block. + $query->operation = trim($xml['operation']) ?: null; + $query->column = trim($xml['column']) ?: null; + $query->pipe = strtoupper($xml['pipe']) ?: 'AND'; // Load tables. foreach ($xml->tables->table as $tag) { if (trim($tag['query']) === 'true') { - $table = self::_parseQuery($tag); - $query->tables[$table->alias] = $table; + $subquery = self::_parseQuery($tag); + $query->tables[$subquery->alias] = $subquery; } else { @@ -77,18 +81,26 @@ class DBQueryParser // Load columns. foreach ($xml->columns->column as $tag) { - $column = new DBQuery\Column; - $column->name = trim($tag['name']); - $column->alias = trim($tag['alias']) ?: null; - if ($column->name === '*' || preg_match('/\.\*$/', $column->name)) + if ($tag->getName() === 'query') { - $column->is_wildcard = true; + $subquery = self::_parseQuery($tag, trim($tag['id'])); + $query->columns[] = $subquery; } - if (!self::_isValidColumnName($column->name)) + else { - $column->is_expression = true; + $column = new DBQuery\Column; + $column->name = trim($tag['name']); + $column->alias = trim($tag['alias']) ?: null; + if ($column->name === '*' || preg_match('/\.\*$/', $column->name)) + { + $column->is_wildcard = true; + } + if (!self::isValidColumnName($column->name)) + { + $column->is_expression = true; + } + $query->columns[] = $column; } - $query->columns[] = $column; } // Load conditions. @@ -131,13 +143,23 @@ class DBQueryParser { if ($tag = $xml->navigation->{$key}) { - $query->navigation->{$key} = new DBQuery\GenericVar; + $query->navigation->{$key} = new DBQuery\VariableBase; $query->navigation->{$key}->var = trim($tag['var']) ?: null; $query->navigation->{$key}->default = trim($tag['default']) ?: null; } } } + // If a SELECT query has no columns, use * by default. + if ($query->type === 'SELECT' && !count($query->columns)) + { + $column = new DBQuery\Column; + $column->name = '*'; + $column->is_wildcard = true; + $column->is_expression = true; + $query->columns[] = $column; + } + // Return the complete query definition. return $query; } @@ -159,19 +181,27 @@ class DBQueryParser $cond = new DBQuery\Condition; $cond->operation = trim($tag['operation']); $cond->column = trim($tag['column']); - $cond->var = trim($tag['var']); - $cond->default = trim($tag['default']); + $cond->var = trim($tag['var']) ?: null; + $cond->default = trim($tag['default']) ?: null; $cond->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; - $cond->operator = strtoupper($tag['pipe']) ?: 'AND'; + $cond->filter = trim($tag['filter']) ?: null; + $cond->minlength = intval(trim($tag['minlength']), 10); + $cond->maxlength = intval(trim($tag['maxlength']), 10); + $cond->pipe = strtoupper($tag['pipe']) ?: 'AND'; $result[] = $cond; } elseif ($name === 'group') { $group = new DBQuery\ConditionGroup; $group->conditions = self::_parseConditions($tag); - $group->operator = strtoupper($tag['pipe']) ?: 'AND'; + $group->pipe = strtoupper($tag['pipe']) ?: 'AND'; $result[] = $group; } + elseif ($name === 'query') + { + $subquery = self::_parseQuery($tag); + $result[] = $subquery; + } } return $result; @@ -183,7 +213,7 @@ class DBQueryParser * @param string $column_name * @return bool */ - protected static function _isValidColumnName(string $column_name): bool + public static function isValidColumnName(string $column_name): bool { if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/', $column_name)) { From 93e597989dd200d8b0fe38ae2bcccaadacee4b3f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 11:14:51 +0900 Subject: [PATCH 06/47] Generate CREATE TABLE queries using new parser --- .../framework/parsers/dbtable/constraint.php | 5 +- common/framework/parsers/dbtable/table.php | 102 ++++++++++++++++++ common/framework/parsers/dbtableparser.php | 15 +-- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/common/framework/parsers/dbtable/constraint.php b/common/framework/parsers/dbtable/constraint.php index c2ceddaa4..8de87ef66 100644 --- a/common/framework/parsers/dbtable/constraint.php +++ b/common/framework/parsers/dbtable/constraint.php @@ -10,6 +10,7 @@ class Constraint public $type; public $column; public $references; - public $on_update; - public $on_delete; + public $condition; + public $on_delete = 'RESTRICT'; + public $on_update = 'RESTRICT'; } diff --git a/common/framework/parsers/dbtable/table.php b/common/framework/parsers/dbtable/table.php index 6dc6d6420..54553d540 100644 --- a/common/framework/parsers/dbtable/table.php +++ b/common/framework/parsers/dbtable/table.php @@ -12,4 +12,106 @@ class Table public $indexes = array(); public $primary_key = array(); public $constraints = array(); + + /** + * Generate the CREATE TABLE query for this table. + * + * @param string $prefix + * @param string $charset + * @param string $engine + * @return string + */ + public function getCreateQuery(string $prefix = '', string $charset = 'utf8mb4', string $engine = 'innodb') + { + // Initialize the query. + $result = 'CREATE TABLE `' . $prefix . $this->name . '` ('; + + // Add columns. + $columns = array(); + foreach ($this->columns as $column) + { + $columndef = ' `' . $column->name . '`' . ' ' . strtoupper($column->type); + if ($column->size) + { + $columndef .= '(' . $column->size . ')'; + } + if ($column->utf8mb4 === false && $charset === 'utf8mb4') + { + $columndef .= ' CHARACTER SET utf8 COLLATE utf8_unicode_ci'; + } + if ($column->not_null) + { + $columndef .= ' NOT NULL'; + } + if ($column->default_value !== null) + { + if (preg_match('/(?:int|float|double|decimal|number)/i', $column->type) && is_numeric($column->default_value)) + { + $columndef .= ' DEFAULT ' . $column->default_value; + } + else + { + $columndef .= ' DEFAULT \'' . $column->default_value . '\''; + } + } + if ($column->auto_increment) + { + $columndef .= ' AUTO_INCREMENT'; + } + $columns[] = $columndef; + } + + // Add indexes. + if (count($this->primary_key)) + { + $pkcolumns = array_map(function($str) { + return '`' . $str . '`'; + }, $this->primary_key); + $pkdef = ' ' . 'PRIMARY KEY (' . implode(', ', $pkcolumns) . ')'; + $columns[] = $pkdef; + } + foreach ($this->indexes as $index) + { + $idxcolumns = array_map(function($str) { + return '`' . $str . '`'; + }, $index->columns); + $idxtype = ($index->is_unique ? 'UNIQUE' : 'INDEX'); + $idxdef = ' ' . $idxtype . ' `' . $index->name . '` (' . implode(', ', $idxcolumns) . ')'; + $columns[] = $idxdef; + } + + // Add constraints. + foreach ($this->constraints as $constraint) + { + $contype = strtoupper($constraint->type); + if ($contype === 'FOREIGN KEY') + { + $condef = ' ' . $contype . ' (`' . $constraint->column . '`)'; + list($reftable, $refcolumn) = explode('.', $constraint->references); + $condef .= ' REFERENCES `' . $prefix . $reftable . '` (`' . $refcolumn . '`)'; + $condef .= ' ON DELETE ' . strtoupper($constraint->on_delete); + $condef .= ' ON UPDATE ' . strtoupper($constraint->on_update); + } + if ($contype === 'CHECK') + { + $condef = ' ' . $contype . ' (' . $constraint->condition . ')'; + } + $columns[] = $condef; + } + + // Finish the query. + $footer = ''; + if ($engine) + { + $footer .= ' ENGINE = ' . (strtolower($engine) === 'innodb' ? 'InnoDB' : 'MyISAM'); + } + if ($charset) + { + $footer .= ' CHARACTER SET ' . $charset . ' COLLATE ' . $charset . '_unicode_ci'; + } + $result .= "\n" . implode(",\n", $columns); + $result .= "\n" . ') ' . $footer . ';'; + + return $result; + } } diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index 3fda78870..deb51e590 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -159,13 +159,14 @@ class DBTableParser // Load other constraints (foreign keys). foreach ($xml->constraint as $const_info) { - $const = new DBTable\Constraint; - $const->type = strtolower($const_info['type']); - $const->column = strval($const_info['column']); - $const->references = strval($const_info['references']); - $const->on_update = strtolower($const_info['on_update'] ?: $const_info['on-update']); - $const->on_delete = strtolower($const_info['on_delete'] ?: $const_info['on-delete']); - $table->constraints[] = $const; + $constraint = new DBTable\Constraint; + $constraint->type = strtolower($const_info['type']); + $constraint->column = strval($const_info['column']) ?: null; + $constraint->references = strval($const_info['references']) ?: null; + $constraint->condition = strval($const_info['condition']) ?: null; + $constraint->on_delete = (strtolower($const_info['on_delete'] ?: $const_info['on-delete'])) ?: $constraint->on_delete; + $constraint->on_update = (strtolower($const_info['on_update'] ?: $const_info['on-update'])) ?: $constraint->on_update; + $table->constraints[] = $constraint; } // Return the complete table definition. From 93be42c4771ed902bb9bf9e54c5815b40a832d8f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 13:46:48 +0900 Subject: [PATCH 07/47] Restrict index size to 191 chars for utf8mb4 and 255 chars for utf8 (767 chars total) --- common/framework/parsers/dbtable/column.php | 2 + common/framework/parsers/dbtable/table.php | 23 +++++++++-- common/framework/parsers/dbtableparser.php | 43 ++++++++++++++++++--- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/common/framework/parsers/dbtable/column.php b/common/framework/parsers/dbtable/column.php index 119c9b0a2..51df96f69 100644 --- a/common/framework/parsers/dbtable/column.php +++ b/common/framework/parsers/dbtable/column.php @@ -14,6 +14,8 @@ class Column public $utf8mb4 = true; public $default_value; public $not_null = false; + public $is_indexed = false; + public $is_unique = false; public $is_primary_key = false; public $auto_increment = false; } diff --git a/common/framework/parsers/dbtable/table.php b/common/framework/parsers/dbtable/table.php index 54553d540..15a6fdadb 100644 --- a/common/framework/parsers/dbtable/table.php +++ b/common/framework/parsers/dbtable/table.php @@ -28,12 +28,18 @@ class Table // Add columns. $columns = array(); + $adjusted_sizes = array(); foreach ($this->columns as $column) { $columndef = ' `' . $column->name . '`' . ' ' . strtoupper($column->type); + $max_size = $column->utf8mb4 ? 191 : 255; + if (preg_match('/char/i', $column->type) && $column->size > $max_size && ($column->is_unique || $column->is_primary_key)) + { + $adjusted_sizes[$column->name] = $max_size; + } if ($column->size) { - $columndef .= '(' . $column->size . ')'; + $columndef .= '(' . (isset($adjusted_sizes[$column->name]) ? $adjusted_sizes[$column->name] : $column->size) . ')'; } if ($column->utf8mb4 === false && $charset === 'utf8mb4') { @@ -72,9 +78,18 @@ class Table } foreach ($this->indexes as $index) { - $idxcolumns = array_map(function($str) { - return '`' . $str . '`'; - }, $index->columns); + $idxcolumns = array(); + foreach ($index->columns as $column_name => $prefix_size) + { + $column_info = $this->columns[$column_name]; + $current_size = isset($adjusted_sizes[$column->name]) ? $adjusted_sizes[$column->name] : $column->size; + $max_size = $column_info->utf8mb4 ? 191 : 255; + if (preg_match('/char/i', $column->type) && $current_size > $max_size) + { + $prefix_size = $max_size; + } + $idxcolumns[] = '`' . $column_name . '`' . ($prefix_size > 0 ? "($prefix_size)" : ''); + } $idxtype = ($index->is_unique ? 'UNIQUE' : 'INDEX'); $idxdef = ' ' . $idxtype . ' `' . $index->name . '` (' . implode(', ', $idxcolumns) . ')'; $columns[] = $idxdef; diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index deb51e590..9c1bd195a 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -29,12 +29,21 @@ class DBTableParser * Load a table definition XML file. * * @param string $filename + * @param string $content * @return object|false */ - public static function loadXML(string $filename) + public static function loadXML(string $filename = '', string $content = '') { - // Load the XML file. - $xml = simplexml_load_string(file_get_contents($filename)); + // Load the XML content. + if ($content) + { + $xml = simplexml_load_string($content); + } + else + { + $xml = simplexml_load_string(file_get_contents($filename)); + } + if ($xml === false) { return false; @@ -107,7 +116,8 @@ class DBTableParser $table->indexes[$index_name] = new DBTable\Index; $table->indexes[$index_name]->name = $index_name; } - $table->indexes[$index_name]->columns[] = $column->name; + $table->indexes[$index_name]->columns[$column->name] = 0; + $column->is_indexed = true; } if (isset($column_info['unique'])) { @@ -118,7 +128,9 @@ class DBTableParser $table->indexes[$index_name]->name = $index_name; $table->indexes[$index_name]->is_unique = true; } - $table->indexes[$index_name]->columns[] = $column->name; + $table->indexes[$index_name]->columns[$column->name] = 0; + $column->is_indexed = true; + $column->is_unique = true; } // Get primary key information. @@ -128,6 +140,8 @@ class DBTableParser if ($attr === 'primary_key' || $attr === 'primary-key' || toBool($attr)) { $table->primary_key[] = $column->name; + $column->is_indexed = true; + $column->is_unique = true; $column->is_primary_key = true; } } @@ -151,8 +165,25 @@ class DBTableParser { $index = new DBTable\Index; $index->name = strval($index_info['name']); - $index->columns = array_map('trim', explode(',', strval($index_info['columns']))); + $idxcolumns = array_map('trim', explode(',', strval($index_info['columns']))); + foreach ($idxcolumns as $idxcolumn) + { + if (preg_match('/^(\S+)\s*\(([0-9]+)\)$/', $idxcolumn, $matches)) + { + $index->columns[$matches[1]] = intval($matches[2]); + $idxcolumn = $matches[1]; + } + else + { + $index->columns[$idxcolumn] = 0; + } + } $index->is_unique = ($index_info['unique'] === 'unique' || toBool(strval($index_info['unique']))); + if (isset($table->columns[$idxcolumn]) && is_object($table->columns[$idxcolumn])) + { + $table->columns[$idxcolumn]->is_indexed = true; + $table->columns[$idxcolumn]->is_unique = $index->is_unique; + } $table->indexes[$index->name] = $index; } From 92ff69591f1efbd24dfadfdb0cdb6cfcebd1d645 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 13:51:22 +0900 Subject: [PATCH 08/47] Use new parser to create all tables --- classes/db/DBMysql.class.php | 138 ++------------------- common/framework/parsers/dbtableparser.php | 9 +- 2 files changed, 16 insertions(+), 131 deletions(-) diff --git a/classes/db/DBMysql.class.php b/classes/db/DBMysql.class.php index 23eed8740..8729d1135 100644 --- a/classes/db/DBMysql.class.php +++ b/classes/db/DBMysql.class.php @@ -735,139 +735,17 @@ class DBMySQL extends DB */ function _createTable($xml_doc) { - // Parse XML - $oXml = new XmlParser(); - $xml_obj = $oXml->parse($xml_doc); - - // Get table name and column list - $table_name = $xml_obj->table->attrs->name; - if($this->isTableExists($table_name)) - { - return; - } - if(!is_array($xml_obj->table->column)) - { - $columns[] = $xml_obj->table->column; - } - else - { - $columns = $xml_obj->table->column; - } - - // Initialize the list of columns and indexes - $column_schema = array(); - $primary_list = array(); - $unique_list = array(); - $index_list = array(); - - // Process columns - foreach($columns as $column) - { - $name = $column->attrs->name; - $type = $column->attrs->type; - $size = $column->attrs->size; - $notnull = $column->attrs->notnull; - $primary_key = $column->attrs->primary_key; - $index = $column->attrs->index; - $unique = $column->attrs->unique; - $default = $column->attrs->default; - $auto_increment = $column->attrs->auto_increment; - $column_charset = ''; - $index_size_limit = ''; - - // MySQL only supports 767 bytes for indexed columns. - // This is 191 characters in utf8mb4 and 255 characters in utf8. - if($column->attrs->utf8mb4 === 'false' && stripos($type, 'char') !== false) - { - $column_charset = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci'; - } - elseif(($primary_key || $unique || $index) && stripos($type, 'char') !== false) - { - if($size > 255 || ($size > 191 && $this->charset === 'utf8mb4')) - { - if($primary_key || $unique) - { - $size = ($this->charset === 'utf8mb4') ? 191 : 255; - } - else - { - $index_size_limit = '(' . (($this->charset === 'utf8mb4') ? 191 : 255) . ')'; - } - } - } - - // Normalize data type - $type = strtolower($type); - $type = isset($this->column_type[$type]) ? $this->column_type[$type] : $type; - if(in_array($type, ['integer', 'int', 'bigint', 'smallint'])) - { - $size = ''; - } - - $column_schema[$name] = sprintf('`%s` %s%s %s %s %s %s', - $name, - $type, - $size ? "($size)" : '', - $column_charset, - isset($default) ? "DEFAULT '$default'" : '', - $notnull ? 'NOT NULL' : '', - $auto_increment ? 'AUTO_INCREMENT' : '' - ); - - if($primary_key) - { - $primary_list[] = "`$name`"; - } - else if($unique) - { - $unique_list[$unique][] = "`$name`" . $index_size_limit; - } - else if($index) - { - $index_list[$index][] = "`$name`" . $index_size_limit; - } - } - - // Process indexes - if(count($primary_list)) - { - $column_schema[] = sprintf("PRIMARY KEY (%s)", implode($primary_list, ', ')); - } - if(count($unique_list)) - { - foreach($unique_list as $key => $val) - { - $column_schema[] = sprintf("UNIQUE %s (%s)", $key, implode($val, ', ')); - } - } - if(count($index_list)) - { - foreach($index_list as $key => $val) - { - $column_schema[] = sprintf("INDEX %s (%s)", $key, implode($val, ', ')); - } - } - - // Generate table schema - $engine = config('db.master.engine') === 'innodb' ? 'InnoDB' : 'MyISAM'; - $charset = $this->charset ?: 'utf8'; - $collation = $charset . '_unicode_ci'; - $schema = sprintf("CREATE TABLE `%s` (%s) %s", - $this->addQuotes($this->prefix . $table_name), - "\n" . implode($column_schema, ",\n") . "\n", - "ENGINE = $engine CHARACTER SET $charset COLLATE $collation" - ); - - // Execute the complete query - $output = $this->_query($schema); - if($output) - { - return true; - } - else + // Get table definition. + $table = Rhymix\Framework\Parsers\DBTableParser::loadXML('', $xml_doc); + if (!$table) { return false; } + + // Execute the CREATE TABLE query. + $schema = $table->getCreateQuery($this->prefix, $this->charset, config('db.master.engine')); + $output = $this->_query($schema); + return $output ? true : false; } /** diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index 9c1bd195a..bc46a3eca 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -51,7 +51,14 @@ class DBTableParser // Initialize table definition. $table = new DBTable\Table; - $table->name = preg_replace('/\.xml$/', '', basename($filename)); + if ($filename) + { + $table->name = preg_replace('/\.xml$/', '', basename($filename)); + } + else + { + $table->name = strval($xml['name']); + } // Load columns. foreach ($xml->column as $column_info) From 6eca8736c1b58d200c822e649bfcb0860a51821a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 16:52:41 +0900 Subject: [PATCH 09/47] More work on the query parser --- common/framework/exceptions/queryerror.php | 18 ++ .../framework/parsers/dbquery/expression.php | 49 ---- common/framework/parsers/dbquery/query.php | 211 +++++++++++++++++ .../parsers/dbquery/variablebase.php | 214 ++++++++++++++++-- common/framework/parsers/dbqueryparser.php | 4 +- common/framework/parsers/dbtable/table.php | 2 +- 6 files changed, 422 insertions(+), 76 deletions(-) create mode 100644 common/framework/exceptions/queryerror.php delete mode 100644 common/framework/parsers/dbquery/expression.php diff --git a/common/framework/exceptions/queryerror.php b/common/framework/exceptions/queryerror.php new file mode 100644 index 000000000..afc643788 --- /dev/null +++ b/common/framework/exceptions/queryerror.php @@ -0,0 +1,18 @@ +type = $type; - $this->value = $value; - } - - /** - * Return the string representation of this expression. - * - * @return string - */ - public function __toString() - { - switch ($this->type) - { - case 'var': - return ':' . $this->value; - case 'null': - return 'NULL'; - case 'string': - return; - case 'int': - return $this->value; - - - } - } -} diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index fbadb9c4f..e58896c25 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -18,4 +18,215 @@ class Query public $conditions = array(); public $groupby = null; public $navigation = null; + + /** + * Attributes for query generation. + */ + protected $_prefix = ''; + protected $_args = array(); + protected $_column_list = array(); + protected $_params = array(); + protected $_temp_num = 0; + + /** + * Generate the query string for this query. + * + * @param string $prefix + * @param array $args + * @param array $column_list + * @return string + */ + public function getQueryString(string $prefix = '', array $args, array $column_list = []): string + { + // Save the query information. + $this->_prefix = $prefix; + $this->_args = $args; + $this->_column_list = $column_list; + $this->_temp_num = 0; + + // Call different internal methods depending on the query type. + switch ($this->type) + { + case 'SELECT': + return $this->_getSelectQueryString(); + default: + return ''; + } + } + + /** + * Get the query parameters to use with the query string generated above. + * + * @return array + */ + public function getQueryParams() + { + return $this->_params; + } + + /** + * Generate a SELECT query string. + * + * @return string + */ + protected function _getSelectQueryString(): string + { + // Initialize the query string. + $result = 'SELECT '; + + // Compose the column list. + $columns = array(); + if ($this->_column_list) + { + $result .= implode(', ', array_map(function($str) { + return '`' . $str . '`'; + }, $this->_column_list)); + } + else + { + foreach ($this->columns as $column) + { + if ($column instanceof self) + { + $subquery = $column->getQueryString($this->_prefix, $this->_args); + foreach ($column->getQueryParams() as $key => $val) + { + $this->_params[$key] = $val; + } + $columns[] = sprintf('(%s) AS %s', $subquery, self::quoteName($column->alias)); + } + elseif ($column->is_expression && !$column->is_wildcard) + { + $columns[] = $column->name . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : ''); + } + else + { + $columns[] = self::quoteName($column->name) . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : ''); + } + } + $result .= implode(', ', $columns); + } + + // Compose the table list. + $tables = array(); + foreach ($this->tables as $table) + { + if ($table instanceof self) + { + $subquery = $table->getQueryString($this->_prefix, $this->_args); + foreach ($table->getQueryParams() as $key => $val) + { + $this->_params[$key] = $val; + } + $tables[] = sprintf('(%s) AS `%s`', $subquery, $table->alias); + } + else + { + $tabledef = self::quoteName($table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); + if ($table->join_type) + { + $tabledef = $table->join_type . ' ' . $tabledef; + $join_where = $this->_arrangeConditions($table->join_conditions); + if ($join_where !== '') + { + $tabledef = $tabledef . ' ON ' . $join_where; + } + } + $tables[] = $tabledef; + } + } + $result .= ' FROM ' . implode(', ', $tables); + + // Compose the conditions. + if (count($this->conditions)) + { + $where = $this->_arrangeConditions($this->conditions); + if ($where !== '') + { + $result .= ' WHERE ' . $where; + } + } + + // Compose the GROUP BY clause. + + // Compose the LIMIT clause. + + // Return the final query string. + return $result; + } + + /** + * Generate a WHERE clause from a list of conditions. + * + * @param array $conditions + * @return string + */ + protected function _arrangeConditions(array $conditions): string + { + // Initialize the result. + $result = ''; + + // Process each condition. + foreach ($conditions as $condition) + { + // Subquery + if ($condition instanceof self) + { + // TODO + } + + // Condition group + elseif ($condition instanceof ConditionGroup) + { + $condition_string = $this->_arrangeConditions($condition->conditions); + if ($condition_string === '') + { + continue; + } + $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . '(' . $condition_string . ')'; + } + + // Simple condition + else + { + $condition_string = $this->_parseCondition($condition); + if ($condition_string === '') + { + continue; + } + $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . $condition_string; + } + } + + // Return the WHERE clause. + return $result; + } + + /** + * Generate each condition in a WHERE clause. + * + * @param object $condition + * @return string + */ + protected function _parseCondition(Condition $condition): string + { + list($where, $params) = $condition->getQueryStringAndParams($this->_args); + foreach ($params as $key => $val) + { + $this->_params[$key] = $val; + } + return $where; + } + + /** + * Quote a column name. + */ + public static function quoteName($column_name): string + { + $columns = explode('.', $column_name); + $columns = array_map(function($str) { + return $str === '*' ? $str : ('`' . $str . '`'); + }, $columns); + return implode('.', $columns); + } } diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index 215b2361e..6b50ec41b 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -14,63 +14,229 @@ class VariableBase public $default; /** - * Get the value of this variable. + * Convert an operator into real SQL. * - * @return Expression + * @param array $args + * @return array */ - public function getValue(): Expression + public function getQueryStringAndParams(array $args): array { - if ($this->var) + // Return if this method is called on an invalid child class. + if (!isset($this->column) || !isset($this->operation)) { - return new Expression('var', $this->var); + throw new \Rhymix\Framework\Exceptions\QueryError('Invalid invocation of getQueryStringAndParams()'); + } + + // Process the variable or default value. + if ($this->var && isset($args[$this->var]) && !empty($args[$this->var])) + { + $this->filterValue($args[$this->var]); + $value = $args[$this->var]; + } + elseif ($this->default !== null) + { + $value = $this->getDefaultValue(); + } + elseif ($this->not_null) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' is not set'); } else { - return $this->getDefaultValue(); + return ['', []]; } + + // Quote the column name. + $column = Query::quoteName($this->column); + $where = ''; + $params = array(); + + // Prepare the target value. + $list_ops = array('in' => true, 'notin' => true, 'not_in' => true, 'between' => true); + if (isset($list_ops[$this->operation]) && !is_array($value) && $value !== '') + { + $value = explode(',', preg_replace('/[\s\']/', '', $value)); + } + + // Apply the operator. + switch ($this->operation) + { + case 'equal': + $where = sprintf('%s = ?', $column); + $params[] = $value; + break; + case 'like': + $where = sprintf('%s LIKE ?', $column); + $params[] = '%' . $value . '%'; + break; + case 'like_prefix': + case 'like_head': + $where = sprintf('%s LIKE ?', $column); + $params[] = $value . '%'; + break; + case 'like_suffix': + case 'like_tail': + $where = sprintf('%s LIKE ?', $column); + $params[] = '%' . $value; + break; + case 'notlike': + $where = sprintf('%s NOT LIKE ?', $column); + $params[] = '%' . $value . '%'; + break; + case 'notlike_prefix': + case 'notlike_head': + $where = sprintf('%s NOT LIKE ?', $column); + $params[] = $value . '%'; + break; + case 'notlike_suffix': + case 'notlike_tail': + $where = sprintf('%s NOT LIKE ?', $column); + $params[] = '%' . $value; + break; + case 'in': + $count = count($value); + $placeholders = implode(', ', array_fill(0, $count, '?')); + $where = sprintf('%s IN (%s)', $column, $placeholders); + foreach ($value as $item) + { + $params[] = $item; + } + break; + case 'notin': + case 'not_in': + $count = count($value); + $placeholders = implode(', ', array_fill(0, $count, '?')); + $where = sprintf('%s NOT IN (%s)', $column, $placeholders); + foreach ($value as $item) + { + $params[] = $item; + } + break; + case 'between': + $where = sprintf('%s BETWEEN ? AND ?', $column); + foreach ($value as $item) + { + $params[] = $item; + } + break; + } + + // Return the complete condition and parameters. + return [$where, $params]; } /** * Get the default value of this variable. * - * @return Expression + * @return mixed */ - public function getDefaultValue(): Expression + public function getDefaultValue() { - // If the default value is not set, return null. - $val = $this->default; - if ($val === null) + // If the default value is a column name, escape it. + if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)+$/', $this->default)) { - return new Expression('null'); + return Query::quoteName($this->default); + } + elseif (isset($this->column) && preg_match('/_srl$/', $this->column) && !ctype_digit($this->default)) + { + return Query::quoteName($this->default); } // If the default value is a function shortcut, return an appropriate value. - switch ($val) + switch ($this->default) { case 'ipaddress()': - return new Expression('string', \RX_CLIENT_IP); + return "'" . \RX_CLIENT_IP . "'"; case 'unixtime()': - return new Expression('string', time()); + return time(); case 'curdate()': case 'date()': - return new Expression('string', date('YmdHis')); + return "'" . date('YmdHis') . "'"; case 'sequence()': - return new Expression('int', getNextSequence()); + return getNextSequence(); } // If the default value is a calculation based on the current value, return a query string. - if (isset($this->column) && preg_match('/^(plus|minus|multiply)\(([0-9]+)\)$/', $val, $matches)) + if (isset($this->column) && preg_match('/^(plus|minus|multiply)\(([0-9]+)\)$/', $this->default, $matches)) { - + switch ($matches[1]) + { + case 'plus': + return sprintf('%s + %d', Query::quoteName($this->column), $matches[2]); + case 'minus': + return sprintf('%s - %d', Query::quoteName($this->column), $matches[2]); + case 'multiply': + return sprintf('%s * %d', Query::quoteName($this->column), $matches[2]); + } } - // If the default value is a column name, return the column name.20 - if (\Rhymix\Framework\Parsers\DBQueryParser::isValidColumnName($val)) + // Otherwise, just return the literal value. + return $this->default; + } + + /** + * Filter a value. + * + * @param mixed $value + * @return void + */ + public function filterValue($value) + { + // Apply filters. + switch (isset($this->filter) ? $this->filter : '') { - + case 'email': + case 'email_address': + if (!preg_match('/^[\w-]+((?:\.|\+|\~)[\w-]+)*@[\w-]+(\.[\w-]+)+$/', $value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain a valid e-mail address'); + } + break; + case 'homepage': + case 'url': + if (!preg_match('/^(http|https)+(:\/\/)+[0-9a-z_-]+\.[^ ]+$/i', $value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain a valid URL'); + } + break; + case 'userid': + case 'user_id': + if (!preg_match('/^[a-zA-Z]+([_0-9a-zA-Z]+)*$/', $value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain a valid user ID'); + } + break; + case 'number': + case 'numbers': + if (!preg_match('/^(-?)[0-9]+(,\-?[0-9]+)*$/', is_array($value) ? implode(',', $value) : $value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain a valid number'); + } + break; + case 'alpha': + if (!ctype_alpha($value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain only alphabets'); + } + break; + case 'alnum': + case 'alpha_number': + if (!ctype_alnum($value)) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain only alphanumeric characters'); + } + break; } - // Otherwise, return the literal value. - return new Expression('string', $val); + // Check minimum and maximum lengths. + $length = iconv_strlen($value, 'UTF-8'); + if (isset($this->minlength) && $this->minlength > 0 && $length < $this->minlength) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain no less than ' . $this->minlength . ' characters'); + } + if (isset($this->maxlength) && $this->maxlength > 0 && $length > $this->maxlength) + { + throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain no more than ' . $this->minlength . ' characters'); + } } } diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index d4cd3a28f..7705c1224 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -45,11 +45,11 @@ class DBQueryParser { $query->name = $query->alias; } - $query->type = strtoupper($xml['action']) ?: null; + $query->type = strtoupper($xml['action']) ?: 'SELECT'; // Load attributes that only apply to subqueries in the block. $query->operation = trim($xml['operation']) ?: null; - $query->column = trim($xml['column']) ?: null; + $query->column = preg_replace('/[^a-z0-9_\.]/i', '', $xml['column']) ?: null; $query->pipe = strtoupper($xml['pipe']) ?: 'AND'; // Load tables. diff --git a/common/framework/parsers/dbtable/table.php b/common/framework/parsers/dbtable/table.php index 15a6fdadb..492fab9e5 100644 --- a/common/framework/parsers/dbtable/table.php +++ b/common/framework/parsers/dbtable/table.php @@ -21,7 +21,7 @@ class Table * @param string $engine * @return string */ - public function getCreateQuery(string $prefix = '', string $charset = 'utf8mb4', string $engine = 'innodb') + public function getCreateQuery(string $prefix = '', string $charset = 'utf8mb4', string $engine = 'innodb'): string { // Initialize the query. $result = 'CREATE TABLE `' . $prefix . $this->name . '` ('; From db13d110b87450d858b29da3bf1e77dc259cc1b5 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 20:56:32 +0900 Subject: [PATCH 10/47] Finish work on condition parsing --- common/framework/parsers/dbquery/query.php | 45 ++--- .../parsers/dbquery/variablebase.php | 160 ++++++++++++++---- 2 files changed, 154 insertions(+), 51 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index e58896c25..cbee06fae 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -5,7 +5,7 @@ namespace Rhymix\Framework\Parsers\DBQuery; /** * Query class. */ -class Query +class Query extends VariableBase { public $name; public $alias; @@ -89,9 +89,9 @@ class Query if ($column instanceof self) { $subquery = $column->getQueryString($this->_prefix, $this->_args); - foreach ($column->getQueryParams() as $key => $val) + foreach ($column->getQueryParams() as $param) { - $this->_params[$key] = $val; + $this->_params[] = $param; } $columns[] = sprintf('(%s) AS %s', $subquery, self::quoteName($column->alias)); } @@ -114,28 +114,31 @@ class Query if ($table instanceof self) { $subquery = $table->getQueryString($this->_prefix, $this->_args); - foreach ($table->getQueryParams() as $key => $val) + foreach ($table->getQueryParams() as $param) { - $this->_params[$key] = $val; + $this->_params[] = $param; } - $tables[] = sprintf('(%s) AS `%s`', $subquery, $table->alias); + $tables[] = (count($tables) ? ', ' : '') . sprintf('(%s) AS `%s`', $subquery, $table->alias); } else { $tabledef = self::quoteName($table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); if ($table->join_type) { - $tabledef = $table->join_type . ' ' . $tabledef; $join_where = $this->_arrangeConditions($table->join_conditions); if ($join_where !== '') { $tabledef = $tabledef . ' ON ' . $join_where; } + $tables[] = ' ' . $table->join_type . ' ' . $tabledef; + } + else + { + $tables[] = (count($tables) ? ', ' : '') . $tabledef; } - $tables[] = $tabledef; } } - $result .= ' FROM ' . implode(', ', $tables); + $result .= ' FROM ' . implode('', $tables); // Compose the conditions. if (count($this->conditions)) @@ -172,29 +175,31 @@ class Query // Subquery if ($condition instanceof self) { - // TODO + $condition_string = $this->_parseCondition($condition); + if ($condition_string !== '') + { + $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . $condition_string; + } } // Condition group elseif ($condition instanceof ConditionGroup) { $condition_string = $this->_arrangeConditions($condition->conditions); - if ($condition_string === '') + if ($condition_string !== '') { - continue; + $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . '(' . $condition_string . ')'; } - $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . '(' . $condition_string . ')'; } // Simple condition else { $condition_string = $this->_parseCondition($condition); - if ($condition_string === '') + if ($condition_string !== '') { - continue; + $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . $condition_string; } - $result .= ($result === '' ? '' : (' ' . $condition->pipe . ' ')) . $condition_string; } } @@ -208,12 +213,12 @@ class Query * @param object $condition * @return string */ - protected function _parseCondition(Condition $condition): string + protected function _parseCondition(VariableBase $condition): string { - list($where, $params) = $condition->getQueryStringAndParams($this->_args); - foreach ($params as $key => $val) + list($where, $params) = $condition->getQueryStringAndParams($this->_args, $this->_prefix); + foreach ($params as $param) { - $this->_params[$key] = $val; + $this->_params[] = $param; } return $where; } diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index 6b50ec41b..bccfeaaa0 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -17,9 +17,10 @@ class VariableBase * Convert an operator into real SQL. * * @param array $args + * @param string $prefix * @return array */ - public function getQueryStringAndParams(array $args): array + public function getQueryStringAndParams(array $args, string $prefix = ''): array { // Return if this method is called on an invalid child class. if (!isset($this->column) || !isset($this->operation)) @@ -27,15 +28,26 @@ class VariableBase throw new \Rhymix\Framework\Exceptions\QueryError('Invalid invocation of getQueryStringAndParams()'); } + // Initialze the return values. + $where = ''; + $params = array(); + // Process the variable or default value. - if ($this->var && isset($args[$this->var]) && !empty($args[$this->var])) + if ($this->var && isset($args[$this->var]) && (!is_array($args[$this->var]) || count($args[$this->var]) > 1 || $args[$this->var] !== [''])) { $this->filterValue($args[$this->var]); + $is_expression = false; $value = $args[$this->var]; } elseif ($this->default !== null) { - $value = $this->getDefaultValue(); + list($is_expression, $value) = $this->getDefaultValue(); + } + elseif ($this instanceof Query) + { + $is_expression = true; + $value = '(' . $this->getQueryString($prefix, $args) . ') AS ' . Query::quoteName($this->alias); + $params = $this->getQueryParams(); } elseif ($this->not_null) { @@ -43,13 +55,11 @@ class VariableBase } else { - return ['', []]; + return [$where, $params]; } // Quote the column name. $column = Query::quoteName($this->column); - $where = ''; - $params = array(); // Prepare the target value. $list_ops = array('in' => true, 'notin' => true, 'not_in' => true, 'between' => true); @@ -62,36 +72,93 @@ class VariableBase switch ($this->operation) { case 'equal': - $where = sprintf('%s = ?', $column); - $params[] = $value; + $where = sprintf('%s = %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'notequal': + case 'not_equal': + $where = sprintf('%s != %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'more': + case 'gte': + $where = sprintf('%s >= %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'excess': + case 'gt'; + $where = sprintf('%s > %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'less': + case 'lte': + $where = sprintf('%s <= %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'below': + case 'lt'; + $where = sprintf('%s < %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'regexp'; + $where = sprintf('%s REGEXP %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'notregexp'; + case 'not_regexp'; + $where = sprintf('%s NOT REGEXP %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; break; case 'like': - $where = sprintf('%s LIKE ?', $column); - $params[] = '%' . $value . '%'; + $where = sprintf('%s LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value . '%'; break; case 'like_prefix': case 'like_head': - $where = sprintf('%s LIKE ?', $column); - $params[] = $value . '%'; + $where = sprintf('%s LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value . '%'; break; case 'like_suffix': case 'like_tail': - $where = sprintf('%s LIKE ?', $column); - $params[] = '%' . $value; + $where = sprintf('%s LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; break; case 'notlike': - $where = sprintf('%s NOT LIKE ?', $column); - $params[] = '%' . $value . '%'; + $where = sprintf('%s NOT LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value . '%'; break; case 'notlike_prefix': case 'notlike_head': - $where = sprintf('%s NOT LIKE ?', $column); - $params[] = $value . '%'; + $where = sprintf('%s NOT LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value . '%'; break; case 'notlike_suffix': case 'notlike_tail': - $where = sprintf('%s NOT LIKE ?', $column); - $params[] = '%' . $value; + $where = sprintf('%s NOT LIKE %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; + break; + case 'and': + $where = sprintf('%s & %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; + break; + case 'or': + $where = sprintf('%s | %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; + break; + case 'xor': + $where = sprintf('%s ^ %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; + break; + case 'not': + $where = sprintf('%s ~ %s', $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = '%' . $value; + break; + case 'null': + $where = sprintf('%s IS NULL', $column); + break; + case 'notnull': + case 'not_null': + $where = sprintf('%s IS NOT NULL', $column); break; case 'in': $count = count($value); @@ -119,6 +186,37 @@ class VariableBase $params[] = $item; } break; + case 'notbetween': + case 'not_between': + $where = sprintf('%s NOT BETWEEN ? AND ?', $column); + foreach ($value as $item) + { + $params[] = $item; + } + break; + case 'search': + $keywords = preg_split('/[\s,]+/', $value, 10, \PREG_SPLIT_NO_EMPTY); + $conditions = array(); + $placeholders = implode(', ', array_fill(0, count($keywords), '?')); + foreach ($keywords as $item) + { + if (substr($item, 0, 1) === '-') + { + $conditions[] = sprintf('%s NOT LIKE ?', $column); + $item = substr($item, 1); + } + else + { + $conditions[] = sprintf('%s LIKE ?', $column); + } + $params[] = '%' . str_replace(['\\', '_', '%'], ['\\\\', '\_', '\%'], $item) . '%'; + } + $conditions = implode(' AND ', $conditions); + $where = count($keywords) === 1 ? $conditions : "($conditions)"; + break; + default: + $where = sprintf('%s = ?', $column); + $params[] = $value; } // Return the complete condition and parameters. @@ -128,32 +226,32 @@ class VariableBase /** * Get the default value of this variable. * - * @return mixed + * @return array */ public function getDefaultValue() { // If the default value is a column name, escape it. if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)+$/', $this->default)) { - return Query::quoteName($this->default); + return [true, Query::quoteName($this->default)]; } elseif (isset($this->column) && preg_match('/_srl$/', $this->column) && !ctype_digit($this->default)) { - return Query::quoteName($this->default); + return [true, Query::quoteName($this->default)]; } // If the default value is a function shortcut, return an appropriate value. switch ($this->default) { case 'ipaddress()': - return "'" . \RX_CLIENT_IP . "'"; + return [false, \RX_CLIENT_IP]; case 'unixtime()': - return time(); + return [false, time()]; case 'curdate()': case 'date()': - return "'" . date('YmdHis') . "'"; + return [false, date('YmdHis')]; case 'sequence()': - return getNextSequence(); + return [false, getNextSequence()]; } // If the default value is a calculation based on the current value, return a query string. @@ -162,16 +260,16 @@ class VariableBase switch ($matches[1]) { case 'plus': - return sprintf('%s + %d', Query::quoteName($this->column), $matches[2]); + return [true, sprintf('%s + %d', Query::quoteName($this->column), $matches[2])]; case 'minus': - return sprintf('%s - %d', Query::quoteName($this->column), $matches[2]); + return [true, sprintf('%s - %d', Query::quoteName($this->column), $matches[2])]; case 'multiply': - return sprintf('%s * %d', Query::quoteName($this->column), $matches[2]); + return [true, sprintf('%s * %d', Query::quoteName($this->column), $matches[2])]; } } // Otherwise, just return the literal value. - return $this->default; + return [false, $this->default]; } /** From bc287b0e0fbe84442d80aa5ccc8632ae782a52f9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 21:03:53 +0900 Subject: [PATCH 11/47] Parse GROUP BY and HAVING clauses --- common/framework/parsers/dbquery/query.php | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index cbee06fae..0f813fa4c 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -151,6 +151,30 @@ class Query extends VariableBase } // Compose the GROUP BY clause. + if ($this->groupby && count($this->groupby->columns)) + { + $columns = array(); + foreach ($this->groupby->columns as $column_name) + { + if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/', $column_name)) + { + $columns[] = self::quoteName($column_name); + } + else + { + $columns[] = $column_name; + } + } + $result .= ' GROUP BY ' . implode(', ', $columns); + } + if ($this->groupby && count($this->groupby->having)) + { + $having = $this->_arrangeConditions($this->groupby->having); + if ($having !== '') + { + $result .= ' HAVING ' . $having; + } + } // Compose the LIMIT clause. From 6d251dfbe1b419ae24d6166a28944812a4a8d521 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 26 Jun 2020 21:56:25 +0900 Subject: [PATCH 12/47] Finish the SELECT query generator --- common/framework/parsers/dbquery/query.php | 122 ++++++++++++++++-- .../parsers/dbquery/variablebase.php | 25 +++- 2 files changed, 135 insertions(+), 12 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index 0f813fa4c..518509695 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -26,7 +26,6 @@ class Query extends VariableBase protected $_args = array(); protected $_column_list = array(); protected $_params = array(); - protected $_temp_num = 0; /** * Generate the query string for this query. @@ -34,24 +33,32 @@ class Query extends VariableBase * @param string $prefix * @param array $args * @param array $column_list + * @param bool $count_only * @return string */ - public function getQueryString(string $prefix = '', array $args, array $column_list = []): string + public function getQueryString(string $prefix = '', array $args, array $column_list = [], bool $count_only = false): string { // Save the query information. $this->_prefix = $prefix; $this->_args = $args; $this->_column_list = $column_list; - $this->_temp_num = 0; + $this->_params = array(); // Call different internal methods depending on the query type. switch ($this->type) { case 'SELECT': - return $this->_getSelectQueryString(); + $result = $this->_getSelectQueryString($count_only); + break; default: - return ''; + $result = ''; } + + // Reset state and return the result. + $this->_prefix = ''; + $this->_args = array(); + $this->_column_list = array(); + return $result; } /** @@ -67,16 +74,21 @@ class Query extends VariableBase /** * Generate a SELECT query string. * + * @param bool $count_only * @return string */ - protected function _getSelectQueryString(): string + protected function _getSelectQueryString(bool $count_only = false): string { // Initialize the query string. $result = 'SELECT '; // Compose the column list. $columns = array(); - if ($this->_column_list) + if ($count_only) + { + $result .= 'COUNT(*) AS `count`'; + } + elseif ($this->_column_list) { $result .= implode(', ', array_map(function($str) { return '`' . $str . '`'; @@ -140,7 +152,7 @@ class Query extends VariableBase } $result .= ' FROM ' . implode('', $tables); - // Compose the conditions. + // Compose the WHERE clause. if (count($this->conditions)) { $where = $this->_arrangeConditions($this->conditions); @@ -156,7 +168,7 @@ class Query extends VariableBase $columns = array(); foreach ($this->groupby->columns as $column_name) { - if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/', $column_name)) + if (self::isValidColumnName($column_name)) { $columns[] = self::quoteName($column_name); } @@ -176,7 +188,58 @@ class Query extends VariableBase } } + // Compose the ORDER BY clause. + if ($this->navigation && count($this->navigation->orderby) && !$count_only) + { + $orderby_list = array(); + foreach ($this->navigation->orderby as $orderby) + { + $column_name = ''; + list($is_expression, $column_name) = $orderby->getValue($this->_args); + if (!$column_name) + { + continue; + } + if (!$is_expression && self::isValidColumnName($column_name)) + { + $column_name = self::quoteName($column_name); + } + + if (isset($this->_args[$orderby->order_var])) + { + $column_order = preg_replace('/[^A-Z]/', '', strtoupper($this->_args[$orderby->order_var])); + } + else + { + $column_order = preg_replace('/[^A-Z]/', '', strtoupper($orderby->order_default)); + } + + $orderby_list[] = $column_name . ' ' . $column_order; + } + $result .= ' ORDER BY ' . implode(', ', $orderby_list); + } + // Compose the LIMIT clause. + if ($this->navigation && $this->navigation->list_count && !$count_only) + { + list($is_expression, $list_count) = $this->navigation->list_count->getValue($this->_args); + if ($list_count > 0) + { + if ($this->navigation->page) + { + list($is_expression, $page) = $this->navigation->page->getValue($this->_args); + } + if ($this->navigation->offset) + { + list($is_expression, $offset) = $this->navigation->offset->getValue($this->_args); + } + if ($page > 0) + { + $offset = $list_count * ($page - 1); + } + $result .= ' LIMIT ' . ($offset > 0 ? (intval($offset) . ', ') : '') . intval($list_count); + } + } // Return the final query string. return $result; @@ -249,8 +312,11 @@ class Query extends VariableBase /** * Quote a column name. + * + * @param string $column_name + * @return string */ - public static function quoteName($column_name): string + public static function quoteName(string $column_name): string { $columns = explode('.', $column_name); $columns = array_map(function($str) { @@ -258,4 +324,40 @@ class Query extends VariableBase }, $columns); return implode('.', $columns); } + + /** + * Check if a column name is valid. + * + * @param string $column_name + * @return bool + */ + public static function isValidColumnName(string $column_name): bool + { + return preg_match('/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/i', $column_name) ? true : false; + } + + /** + * Check if a variable is considered valid for XE compatibility. + * + * @param mixed $var + * @return bool + */ + public static function isValidVariable($var): bool + { + if ($var === null || $var === '') + { + return false; + } + + if (is_array($var)) + { + $count = count($var); + if ($count === 0 || ($count === 1 && reset($var) === '')) + { + return false; + } + } + + return true; + } } diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index bccfeaaa0..c63fb5f06 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -33,7 +33,7 @@ class VariableBase $params = array(); // Process the variable or default value. - if ($this->var && isset($args[$this->var]) && (!is_array($args[$this->var]) || count($args[$this->var]) > 1 || $args[$this->var] !== [''])) + if ($this->var && Query::isValidVariable($args[$this->var])) { $this->filterValue($args[$this->var]); $is_expression = false; @@ -223,6 +223,27 @@ class VariableBase return [$where, $params]; } + /** + * Get the current value, falling back to the default value if necessary. + * + * @param array $args + * @return array + */ + public function getValue(array $args) + { + if ($this->var && Query::isValidVariable($args[$this->var])) + { + $is_expression = false; + $value = $args[$this->var]; + } + elseif ($this->default !== null) + { + list($is_expression, $value) = $this->getDefaultValue(); + } + + return [$is_expression, $value]; + } + /** * Get the default value of this variable. * @@ -231,7 +252,7 @@ class VariableBase public function getDefaultValue() { // If the default value is a column name, escape it. - if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)+$/', $this->default)) + if (strpos($this->default, '.') !== false && Query::isValidColumnName($this->default)) { return [true, Query::quoteName($this->default)]; } From e3138f72782b87d23fab6ff51584be8f393b18f7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 00:04:19 +0900 Subject: [PATCH 13/47] Prepare to support INSERT/UPDATE/DELETE queries, too --- .../dbquery/{column.php => columnread.php} | 4 +- .../framework/parsers/dbquery/columnwrite.php | 18 +++ common/framework/parsers/dbquery/query.php | 124 ++++++++++++++---- .../parsers/dbquery/variablebase.php | 12 ++ common/framework/parsers/dbqueryparser.php | 44 ++++--- 5 files changed, 157 insertions(+), 45 deletions(-) rename common/framework/parsers/dbquery/{column.php => columnread.php} (80%) create mode 100644 common/framework/parsers/dbquery/columnwrite.php diff --git a/common/framework/parsers/dbquery/column.php b/common/framework/parsers/dbquery/columnread.php similarity index 80% rename from common/framework/parsers/dbquery/column.php rename to common/framework/parsers/dbquery/columnread.php index c6eaea342..1b51a8121 100644 --- a/common/framework/parsers/dbquery/column.php +++ b/common/framework/parsers/dbquery/columnread.php @@ -3,9 +3,9 @@ namespace Rhymix\Framework\Parsers\DBQuery; /** - * Column class. + * ColumnRead class. */ -class Column +class ColumnRead { public $name; public $alias; diff --git a/common/framework/parsers/dbquery/columnwrite.php b/common/framework/parsers/dbquery/columnwrite.php new file mode 100644 index 000000000..49c2efc88 --- /dev/null +++ b/common/framework/parsers/dbquery/columnwrite.php @@ -0,0 +1,18 @@ +_getSelectQueryString($count_only); break; + case 'INSERT': + $result = $this->_getInsertQueryString(); + break; + case 'UPDATE': + $result = $this->_getUpdateQueryString(); + break; + case 'DELETE': + $result = $this->_getDeleteQueryString(); + break; default: $result = ''; } @@ -119,38 +130,15 @@ class Query extends VariableBase $result .= implode(', ', $columns); } - // Compose the table list. - $tables = array(); - foreach ($this->tables as $table) + // Compose the FROM clause. + if (count($this->tables)) { - if ($table instanceof self) + $tables = $this->_arrangeTables($this->tables); + if ($tables !== '') { - $subquery = $table->getQueryString($this->_prefix, $this->_args); - foreach ($table->getQueryParams() as $param) - { - $this->_params[] = $param; - } - $tables[] = (count($tables) ? ', ' : '') . sprintf('(%s) AS `%s`', $subquery, $table->alias); - } - else - { - $tabledef = self::quoteName($table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); - if ($table->join_type) - { - $join_where = $this->_arrangeConditions($table->join_conditions); - if ($join_where !== '') - { - $tabledef = $tabledef . ' ON ' . $join_where; - } - $tables[] = ' ' . $table->join_type . ' ' . $tabledef; - } - else - { - $tables[] = (count($tables) ? ', ' : '') . $tabledef; - } + $result .= ' FROM ' . $tables; } } - $result .= ' FROM ' . implode('', $tables); // Compose the WHERE clause. if (count($this->conditions)) @@ -245,6 +233,86 @@ class Query extends VariableBase return $result; } + /** + * Generate a INSERT query string. + * + * @return string + */ + protected function _getInsertQueryString(): string + { + return ''; + } + + /** + * Generate a UPDATE query string. + * + * @return string + */ + protected function _getUpdateQueryString(): string + { + return ''; + } + + /** + * Generate a DELETE query string. + * + * @return string + */ + protected function _getDeleteQueryString(): string + { + return ''; + } + + /** + * Generate a FROM clause from a list of tables. + * + * @param array $tables + * @return string + */ + protected function _arrangeTables(array $tables): string + { + // Initialize the result. + $result = array(); + + // Process each table definition. + foreach ($tables as $table) + { + // Subquery + if ($table instanceof self) + { + $tabledef = sprintf('(%s) AS `%s`', $table->getQueryString($this->_prefix, $this->_args), $table->alias); + foreach ($table->getQueryParams() as $param) + { + $this->_params[] = $param; + } + } + + // Simple table + else + { + $tabledef = self::quoteName($table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); + } + + // Add join conditions + if ($table->join_type) + { + $join_where = $this->_arrangeConditions($table->join_conditions); + if ($join_where !== '') + { + $tabledef = $tabledef . ' ON ' . $join_where; + } + $result[] = ' ' . $table->join_type . ' ' . $tabledef; + } + else + { + $result[] = (count($result) ? ', ' : '') . $tabledef; + } + } + + // Combine the result and return as a string. + return implode('', $result); + } + /** * Generate a WHERE clause from a list of conditions. * diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index c63fb5f06..6dd93e609 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -214,6 +214,18 @@ class VariableBase $conditions = implode(' AND ', $conditions); $where = count($keywords) === 1 ? $conditions : "($conditions)"; break; + case 'plus': + $where = sprintf('%s = %s + %s', $column, $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'minus': + $where = sprintf('%s = %s - %s', $column, $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; + case 'multiply': + $where = sprintf('%s = %s * %s', $column, $column, $is_expression ? $value : '?'); + if (!$is_expression) $params[] = $value; + break; default: $where = sprintf('%s = ?', $column); $params[] = $value; diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index 7705c1224..a065a6875 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -57,25 +57,26 @@ class DBQueryParser { if (trim($tag['query']) === 'true') { - $subquery = self::_parseQuery($tag); - $query->tables[$subquery->alias] = $subquery; + $table = self::_parseQuery($tag); + $query->tables[$table->alias] = $table; } else { $table = new DBQuery\Table; $table->name = trim($tag['name']); $table->alias = trim($tag['alias']) ?: $table->name; - $table_type = trim($tag['type']); - if (stripos($table_type, 'join') !== false) - { - $table->join_type = strtoupper($table_type); - if ($tag->conditions) - { - $table->join_conditions = self::_parseConditions($tag->conditions); - } - } - $query->tables[$table->alias] = $table; } + + $table_type = trim($tag['type']); + if (stripos($table_type, 'join') !== false) + { + $table->join_type = strtoupper($table_type); + if ($tag->conditions) + { + $table->join_conditions = self::_parseConditions($tag->conditions); + } + } + $query->tables[$table->alias] = $table; } // Load columns. @@ -86,9 +87,9 @@ class DBQueryParser $subquery = self::_parseQuery($tag, trim($tag['id'])); $query->columns[] = $subquery; } - else + elseif ($query->type === 'SELECT') { - $column = new DBQuery\Column; + $column = new DBQuery\ColumnRead; $column->name = trim($tag['name']); $column->alias = trim($tag['alias']) ?: null; if ($column->name === '*' || preg_match('/\.\*$/', $column->name)) @@ -101,6 +102,19 @@ class DBQueryParser } $query->columns[] = $column; } + else + { + $column = new DBQuery\ColumnWrite; + $column->name = trim($tag['name']); + $column->name = trim($tag['operation']) ?: 'equal'; + $column->var = trim($tag['var']) ?: null; + $column->default = trim($tag['default']) ?: null; + $column->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; + $column->filter = trim($tag['filter']) ?: null; + $column->minlength = intval(trim($tag['minlength']), 10); + $column->maxlength = intval(trim($tag['maxlength']), 10); + $query->columns[] = $column; + } } // Load conditions. @@ -153,7 +167,7 @@ class DBQueryParser // If a SELECT query has no columns, use * by default. if ($query->type === 'SELECT' && !count($query->columns)) { - $column = new DBQuery\Column; + $column = new DBQuery\ColumnRead; $column->name = '*'; $column->is_wildcard = true; $column->is_expression = true; From a086c939355fdbb3c3de16bc515db55d0ccdba4d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 00:08:48 +0900 Subject: [PATCH 14/47] Support SELECT DISTINCT queries --- common/framework/parsers/dbquery/query.php | 5 +++++ common/framework/parsers/dbqueryparser.php | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index 7aa8ec968..d6af79743 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -20,6 +20,7 @@ class Query extends VariableBase public $conditions = array(); public $groupby = null; public $navigation = null; + public $select_distinct = false; /** * Attributes for query generation. @@ -92,6 +93,10 @@ class Query extends VariableBase { // Initialize the query string. $result = 'SELECT '; + if ($this->select_distinct) + { + $result .= 'DISTINCT '; + } // Compose the column list. $columns = array(); diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index a065a6875..c28b927ca 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -80,6 +80,13 @@ class DBQueryParser } // Load columns. + if ($xml->columns && $select_distinct = trim($xml->columns['distinct'])) + { + if ($select_distinct === 'distinct' || toBool($select_distinct)) + { + $query->select_distinct = true; + } + } foreach ($xml->columns->column as $tag) { if ($tag->getName() === 'query') From 03279788da87cdfd14e3c6a1c86f8ab669f18382 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 00:32:23 +0900 Subject: [PATCH 15/47] Implement DELETE queries using new parser --- common/framework/parsers/dbquery/query.php | 165 +++++++++++++++------ 1 file changed, 118 insertions(+), 47 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index d6af79743..db782f169 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -184,54 +184,13 @@ class Query extends VariableBase // Compose the ORDER BY clause. if ($this->navigation && count($this->navigation->orderby) && !$count_only) { - $orderby_list = array(); - foreach ($this->navigation->orderby as $orderby) - { - $column_name = ''; - list($is_expression, $column_name) = $orderby->getValue($this->_args); - if (!$column_name) - { - continue; - } - if (!$is_expression && self::isValidColumnName($column_name)) - { - $column_name = self::quoteName($column_name); - } - - if (isset($this->_args[$orderby->order_var])) - { - $column_order = preg_replace('/[^A-Z]/', '', strtoupper($this->_args[$orderby->order_var])); - } - else - { - $column_order = preg_replace('/[^A-Z]/', '', strtoupper($orderby->order_default)); - } - - $orderby_list[] = $column_name . ' ' . $column_order; - } - $result .= ' ORDER BY ' . implode(', ', $orderby_list); + $result .= ' ORDER BY ' . $this->_arrangeOrderBy($this->navigation); } - // Compose the LIMIT clause. + // Compose the LIMIT/OFFSET clause. if ($this->navigation && $this->navigation->list_count && !$count_only) { - list($is_expression, $list_count) = $this->navigation->list_count->getValue($this->_args); - if ($list_count > 0) - { - if ($this->navigation->page) - { - list($is_expression, $page) = $this->navigation->page->getValue($this->_args); - } - if ($this->navigation->offset) - { - list($is_expression, $offset) = $this->navigation->offset->getValue($this->_args); - } - if ($page > 0) - { - $offset = $list_count * ($page - 1); - } - $result .= ' LIMIT ' . ($offset > 0 ? (intval($offset) . ', ') : '') . intval($list_count); - } + $result .= ' LIMIT ' . $this->_arrangeLimitOffset($this->navigation); } // Return the final query string. @@ -265,7 +224,43 @@ class Query extends VariableBase */ protected function _getDeleteQueryString(): string { - return ''; + // Initialize the query string. + $result = 'DELETE'; + + // Compose the FROM clause. + if (count($this->tables)) + { + $tables = $this->_arrangeTables($this->tables); + if ($tables !== '') + { + $result .= ' FROM ' . $tables; + } + } + + // Compose the WHERE clause. + if (count($this->conditions)) + { + $where = $this->_arrangeConditions($this->conditions); + if ($where !== '') + { + $result .= ' WHERE ' . $where; + } + } + + // Compose the ORDER BY clause. + if ($this->navigation && count($this->navigation->orderby)) + { + $result .= ' ORDER BY ' . $this->_arrangeOrderBy($this->navigation); + } + + // Compose the LIMIT/OFFSET clause. + if ($this->navigation && $this->navigation->list_count) + { + $result .= ' LIMIT ' . $this->_arrangeLimitOffset($this->navigation); + } + + // Return the final query string. + return $result; } /** @@ -292,10 +287,10 @@ class Query extends VariableBase } } - // Simple table + // Regular table else { - $tabledef = self::quoteName($table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); + $tabledef = self::quoteName($this->_prefix . $table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); } // Add join conditions @@ -367,6 +362,82 @@ class Query extends VariableBase return $result; } + /** + * Generate a ORDER BY clause from navigation settings. + * + * @param object $navigation + * @return string + */ + protected function _arrangeOrderBy(Navigation $navigation): string + { + // Initialize the result. + $result = array(); + + // Process each column definition. + foreach ($navigation->orderby as $orderby) + { + // Get the name of the column or expression to order by. + $column_name = ''; + list($is_expression, $column_name) = $orderby->getValue($this->_args); + if (!$column_name) + { + continue; + } + if (!$is_expression && self::isValidColumnName($column_name)) + { + $column_name = self::quoteName($column_name); + } + + // Get the ordering (ASC or DESC). + if (isset($this->_args[$orderby->order_var])) + { + $column_order = preg_replace('/[^A-Z]/', '', strtoupper($this->_args[$orderby->order_var])); + } + else + { + $column_order = preg_replace('/[^A-Z]/', '', strtoupper($orderby->order_default)); + } + + $result[] = $column_name . ' ' . $column_order; + } + + // Return the ORDER BY clause. + return implode(', ', $result); + } + + /** + * Generate a LIMIT/OFFSET clause from navigation settings. + * + * @param object $navigation + * @return string + */ + protected function _arrangeLimitOffset(Navigation $navigation): string + { + // Get the list count. + list($is_expression, $list_count) = $navigation->list_count->getValue($this->_args); + if ($list_count <= 0) + { + return ''; + } + + // Get the offset from the page or offset variable. + if ($navigation->page) + { + list($is_expression, $page) = $navigation->page->getValue($this->_args); + } + if ($navigation->offset) + { + list($is_expression, $offset) = $navigation->offset->getValue($this->_args); + } + if ($page > 0) + { + $offset = $list_count * ($page - 1); + } + + // Return the LIMIT/OFFSET clause. + return ($offset > 0 ? (intval($offset) . ', ') : '') . intval($list_count); + } + /** * Generate each condition in a WHERE clause. * From 7f8312ee1a6454e4b910bf2ab3744e5ec7c7ac95 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 00:49:04 +0900 Subject: [PATCH 16/47] Implement INSERT and UPDATE queries using new parser --- common/framework/parsers/dbquery/query.php | 66 ++++++++++++++++++- .../parsers/dbquery/variablebase.php | 4 +- common/framework/parsers/dbqueryparser.php | 2 +- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index db782f169..3388d000d 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -204,7 +204,33 @@ class Query extends VariableBase */ protected function _getInsertQueryString(): string { - return ''; + // Initialize the query string. + $result = 'INSERT'; + + // Compose the INTO clause. + if (count($this->tables)) + { + $tables = $this->_arrangeTables($this->tables); + if ($tables !== '') + { + $result .= ' INTO ' . $tables; + } + } + + // Process the SET clause with new values. + $columns = array(); + foreach ($this->columns as $column) + { + $setval_string = $this->_parseCondition($column); + if ($setval_string !== '') + { + $columns[] = $setval_string; + } + } + $result .= ' SET ' . implode(', ', $columns); + + // Return the final query string. + return $result; } /** @@ -214,7 +240,43 @@ class Query extends VariableBase */ protected function _getUpdateQueryString(): string { - return ''; + // Initialize the query string. + $result = 'UPDATE '; + + // Compose the INTO clause. + if (count($this->tables)) + { + $tables = $this->_arrangeTables($this->tables); + if ($tables !== '') + { + $result .= $tables; + } + } + + // Compose the SET clause with updated values. + $columns = array(); + foreach ($this->columns as $column) + { + $setval_string = $this->_parseCondition($column); + if ($setval_string !== '') + { + $columns[] = $setval_string; + } + } + $result .= ' SET ' . implode(', ', $columns); + + // Compose the WHERE clause. + if (count($this->conditions)) + { + $where = $this->_arrangeConditions($this->conditions); + if ($where !== '') + { + $result .= ' WHERE ' . $where; + } + } + + // Return the final query string. + return $result; } /** diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index 6dd93e609..dc3346ece 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -23,7 +23,7 @@ class VariableBase public function getQueryStringAndParams(array $args, string $prefix = ''): array { // Return if this method is called on an invalid child class. - if (!isset($this->column) || !isset($this->operation)) + if ((!isset($this->column) && !isset($this->name)) || !isset($this->operation)) { throw new \Rhymix\Framework\Exceptions\QueryError('Invalid invocation of getQueryStringAndParams()'); } @@ -59,7 +59,7 @@ class VariableBase } // Quote the column name. - $column = Query::quoteName($this->column); + $column = Query::quoteName(isset($this->column) ? $this->column : $this->name); // Prepare the target value. $list_ops = array('in' => true, 'notin' => true, 'not_in' => true, 'between' => true); diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index c28b927ca..b327aaf46 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -113,7 +113,7 @@ class DBQueryParser { $column = new DBQuery\ColumnWrite; $column->name = trim($tag['name']); - $column->name = trim($tag['operation']) ?: 'equal'; + $column->operation = trim($tag['operation']) ?: 'equal'; $column->var = trim($tag['var']) ?: null; $column->default = trim($tag['default']) ?: null; $column->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; From 6f0ecd26a7e646e819c400937de2fa9b4c7cc51c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 00:58:24 +0900 Subject: [PATCH 17/47] Support ON DUPLICATE KEY UPDATE (upsert) queries --- common/framework/parsers/dbquery/query.php | 12 +++++++++++ common/framework/parsers/dbqueryparser.php | 25 ++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index 3388d000d..a1c268bff 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -21,6 +21,7 @@ class Query extends VariableBase public $groupby = null; public $navigation = null; public $select_distinct = false; + public $update_duplicate = false; /** * Attributes for query generation. @@ -229,6 +230,17 @@ class Query extends VariableBase } $result .= ' SET ' . implode(', ', $columns); + // Process the ON DUPLICATE KEY UPDATE (upsert) clause. + if ($this->update_duplicate && count($columns)) + { + $result .= ' ON DUPLICATE KEY UPDATE ' . implode(', ', $columns); + $duplicate_params = $this->_params; + foreach ($duplicate_params as $param) + { + $this->_params[] = $param; + } + } + // Return the final query string. return $result; } diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index b327aaf46..09136be35 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -80,13 +80,6 @@ class DBQueryParser } // Load columns. - if ($xml->columns && $select_distinct = trim($xml->columns['distinct'])) - { - if ($select_distinct === 'distinct' || toBool($select_distinct)) - { - $query->select_distinct = true; - } - } foreach ($xml->columns->column as $tag) { if ($tag->getName() === 'query') @@ -181,6 +174,24 @@ class DBQueryParser $query->columns[] = $column; } + // Check the SELECT DISTINCT flag. + if ($xml->columns && $select_distinct = trim($xml->columns['distinct'])) + { + if ($select_distinct === 'distinct' || toBool($select_distinct)) + { + $query->select_distinct = true; + } + } + + // Check the ON DUPLICATE KEY UPDATE (upsert) flag. + if ($query->type === 'INSERT' && $update_duplicate = trim($xml['update_duplicate'] ?: $xml['update-duplicate'])) + { + if (toBool($update_duplicate)) + { + $query->update_duplicate = true; + } + } + // Return the complete query definition. return $query; } From a1b946bae63bda4b68c1feea3a0e4df465fb54c6 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 01:05:32 +0900 Subject: [PATCH 18/47] Add some useful functions to default values --- common/framework/parsers/dbquery/variablebase.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index dc3346ece..b81251456 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -281,8 +281,14 @@ class VariableBase case 'unixtime()': return [false, time()]; case 'curdate()': - case 'date()': + case 'datetime()': return [false, date('YmdHis')]; + case 'date()': + return [false, date('Ymd')]; + case 'time()': + return [false, date('His')]; + case 'member_srl()': + return [false, intval(\Rhymix\Framework\Session::getMemberSrl())]; case 'sequence()': return [false, getNextSequence()]; } From dcbd26e7f2a3b46ae4524f0bfd32d420d745d493 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 27 Jun 2020 01:25:05 +0900 Subject: [PATCH 19/47] Reorder and categorize query attributes for easy reference --- common/framework/parsers/dbquery/query.php | 25 ++++++++++++++++------ common/framework/parsers/dbqueryparser.php | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index a1c268bff..5b1493318 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -7,14 +7,11 @@ namespace Rhymix\Framework\Parsers\DBQuery; */ class Query extends VariableBase { + /** + * Attributes common to all queries. + */ public $name; - public $alias; public $type; - public $operation; - public $column; - public $pipe; - public $join_type; - public $join_conditions = array(); public $tables = array(); public $columns = array(); public $conditions = array(); @@ -24,7 +21,21 @@ class Query extends VariableBase public $update_duplicate = false; /** - * Attributes for query generation. + * Attributes for subqueries in the or section. + */ + public $alias; + public $join_type; + public $join_conditions = array(); + + /** + * Attributes for subqueries in the section. + */ + public $operation; + public $column; + public $pipe; + + /** + * Attributes used during query string generation. */ protected $_prefix = ''; protected $_args = array(); diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index 09136be35..13e965148 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -40,12 +40,12 @@ class DBQueryParser // Load basic information about this query. $query = new DBQuery\Query; $query->name = $name ?: null; + $query->type = strtoupper($xml['action']) ?: 'SELECT'; $query->alias = trim($xml['alias']) ?: null; if ($query->alias && !$query->name) { $query->name = $query->alias; } - $query->type = strtoupper($xml['action']) ?: 'SELECT'; // Load attributes that only apply to subqueries in the block. $query->operation = trim($xml['operation']) ?: null; From 59b6402ddb086ec7cba64b600f06be4cf2a77be2 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 28 Jun 2020 23:19:57 +0900 Subject: [PATCH 20/47] Remove duplicate method --- common/framework/parsers/dbqueryparser.php | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index 13e965148..055f19ad9 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -96,7 +96,7 @@ class DBQueryParser { $column->is_wildcard = true; } - if (!self::isValidColumnName($column->name)) + if (!DBQuery\Query::isValidColumnName($column->name)) { $column->is_expression = true; } @@ -238,22 +238,4 @@ class DBQueryParser return $result; } - - /** - * Check if an expression might be a valid column name. - * - * @param string $column_name - * @return bool - */ - public static function isValidColumnName(string $column_name): bool - { - if (preg_match('/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/', $column_name)) - { - return true; - } - else - { - return false; - } - } } From 14e91b2e09a5cdea6fb248fef58a649e9adb27f7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 12:30:58 +0900 Subject: [PATCH 21/47] Rewrite DB parsers to use array attributes for better stability --- common/framework/parsers/dbqueryparser.php | 38 ++++++------ common/framework/parsers/dbtableparser.php | 68 ++++++++++------------ 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index 055f19ad9..cc0dcac66 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -5,7 +5,7 @@ namespace Rhymix\Framework\Parsers; /** * DB query parser class for XE compatibility. */ -class DBQueryParser +class DBQueryParser extends BaseParser { /** * Load a query XML file. @@ -38,19 +38,20 @@ class DBQueryParser protected static function _parseQuery(\SimpleXMLElement $xml, string $name = ''): DBQuery\Query { // Load basic information about this query. + $attribs = self::_getAttributes($xml); $query = new DBQuery\Query; $query->name = $name ?: null; - $query->type = strtoupper($xml['action']) ?: 'SELECT'; - $query->alias = trim($xml['alias']) ?: null; + $query->type = strtoupper($attribs['action']) ?: 'SELECT'; + $query->alias = $attribs['alias'] ?? null; if ($query->alias && !$query->name) { $query->name = $query->alias; } // Load attributes that only apply to subqueries in the block. - $query->operation = trim($xml['operation']) ?: null; - $query->column = preg_replace('/[^a-z0-9_\.]/i', '', $xml['column']) ?: null; - $query->pipe = strtoupper($xml['pipe']) ?: 'AND'; + $query->operation = $attribs['operation'] ?? null; + $query->column = preg_replace('/[^a-z0-9_\.]/i', '', $attribs['column']) ?: null; + $query->pipe = strtoupper($attribs['pipe']) ?: 'AND'; // Load tables. foreach ($xml->tables->table as $tag) @@ -109,7 +110,7 @@ class DBQueryParser $column->operation = trim($tag['operation']) ?: 'equal'; $column->var = trim($tag['var']) ?: null; $column->default = trim($tag['default']) ?: null; - $column->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; + $column->not_null = self::_getAttributes($tag)['notnull'] ? true : false; $column->filter = trim($tag['filter']) ?: null; $column->minlength = intval(trim($tag['minlength']), 10); $column->maxlength = intval(trim($tag['maxlength']), 10); @@ -184,7 +185,7 @@ class DBQueryParser } // Check the ON DUPLICATE KEY UPDATE (upsert) flag. - if ($query->type === 'INSERT' && $update_duplicate = trim($xml['update_duplicate'] ?: $xml['update-duplicate'])) + if ($query->type === 'INSERT' && $update_duplicate = $attribs['updateduplicate']) { if (toBool($update_duplicate)) { @@ -207,26 +208,27 @@ class DBQueryParser $result = array(); foreach ($parent->children() as $tag) { + $attribs = self::_getAttributes($tag); $name = $tag->getName(); if ($name === 'condition') { $cond = new DBQuery\Condition; - $cond->operation = trim($tag['operation']); - $cond->column = trim($tag['column']); - $cond->var = trim($tag['var']) ?: null; - $cond->default = trim($tag['default']) ?: null; - $cond->not_null = trim($tag['notnull'] ?: $tag['not-null']) !== '' ? true : false; - $cond->filter = trim($tag['filter']) ?: null; - $cond->minlength = intval(trim($tag['minlength']), 10); - $cond->maxlength = intval(trim($tag['maxlength']), 10); - $cond->pipe = strtoupper($tag['pipe']) ?: 'AND'; + $cond->operation = $attribs['operation']; + $cond->column = $attribs['column']; + $cond->var = $attribs['var'] ?? null; + $cond->default = $attribs['default'] ?? null; + $cond->not_null = $attribs['notnull'] ? true : false; + $cond->filter = $attribs['filter'] ?? null; + $cond->minlength = intval($attribs['minlength'], 10); + $cond->maxlength = intval($attribs['maxlength'], 10); + $cond->pipe = strtoupper($attribs['pipe']) ?: 'AND'; $result[] = $cond; } elseif ($name === 'group') { $group = new DBQuery\ConditionGroup; $group->conditions = self::_parseConditions($tag); - $group->pipe = strtoupper($tag['pipe']) ?: 'AND'; + $group->pipe = strtoupper($attribs['pipe']) ?: 'AND'; $result[] = $group; } elseif ($name === 'query') diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index bc46a3eca..71475e09b 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -5,7 +5,7 @@ namespace Rhymix\Framework\Parsers; /** * DB table parser class for XE compatibility. */ -class DBTableParser +class DBTableParser extends BaseParser { /** * Mapping for XE-compatible types. @@ -95,29 +95,31 @@ class DBTableParser $column->size = null; } + // Get all attributes. + $attribs = self::_getAttributes($column_info); + // Get the utf8mb4 attribute. - if (isset($column_info['utf8mb4'])) + if (isset($attribs['utf8mb4'])) { - $column->utf8mb4 = toBool(strval($column_info['utf8mb4'])); + $column->utf8mb4 = toBool($attribs['utf8mb4']); } // Get the default value. - if (isset($column_info['default'])) + if (isset($attribs['default'])) { - $column->default_value = strval($column_info['default']); + $column->default_value = $attribs['default']; } // Get the NOT NULL attribute. - if (isset($column_info['notnull']) || isset($column_info['not-null'])) + if (isset($attribs['notnull'])) { - $attr = strval($column_info['notnull'] ?: $column_info['not-null']); - $column->not_null = ($attr === 'notnull' || $attr === 'not-null' || toBool($attr)); + $column->not_null = true; } // Get index information. - if (isset($column_info['index'])) + if (isset($attribs['index'])) { - $index_name = strval($column_info['index']); + $index_name = $attribs['index']; if (!isset($table->indexes[$index_name])) { $table->indexes[$index_name] = new DBTable\Index; @@ -126,9 +128,9 @@ class DBTableParser $table->indexes[$index_name]->columns[$column->name] = 0; $column->is_indexed = true; } - if (isset($column_info['unique'])) + if (isset($attribs['unique'])) { - $index_name = strval($column_info['unique']); + $index_name = $attribs['unique']; if (!isset($table->indexes[$index_name])) { $table->indexes[$index_name] = new DBTable\Index; @@ -141,26 +143,18 @@ class DBTableParser } // Get primary key information. - if (isset($column_info['primary_key']) || isset($column_info['primary-key'])) + if (isset($attribs['primarykey']) && toBool($attribs['primarykey'])) { - $attr = strval($column_info['primary_key'] ?: $column_info['primary-key']); - if ($attr === 'primary_key' || $attr === 'primary-key' || toBool($attr)) - { - $table->primary_key[] = $column->name; - $column->is_indexed = true; - $column->is_unique = true; - $column->is_primary_key = true; - } + $table->primary_key[] = $column->name; + $column->is_indexed = true; + $column->is_unique = true; + $column->is_primary_key = true; } // Get auto-increment information. - if (isset($column_info['auto_increment']) || isset($column_info['auto-increment'])) + if (isset($attribs['autoincrement']) && toBool($attribs['autoincrement'])) { - $attr = strval($column_info['auto_increment'] ?: $column_info['auto-increment']); - if ($attr === 'auto_increment' || $attr === 'auto-increment' || toBool($attr)) - { - $column->auto_increment = true; - } + $column->auto_increment = true; } // Add the column to the table definition. @@ -170,9 +164,10 @@ class DBTableParser // Load indexes. foreach ($xml->index as $index_info) { + $index_info = self::_getAttributes($index_info); $index = new DBTable\Index; - $index->name = strval($index_info['name']); - $idxcolumns = array_map('trim', explode(',', strval($index_info['columns']))); + $index->name = $index_info['name']; + $idxcolumns = array_map('trim', explode(',', $index_info['columns'] ?? $index_info['column'])); foreach ($idxcolumns as $idxcolumn) { if (preg_match('/^(\S+)\s*\(([0-9]+)\)$/', $idxcolumn, $matches)) @@ -185,7 +180,7 @@ class DBTableParser $index->columns[$idxcolumn] = 0; } } - $index->is_unique = ($index_info['unique'] === 'unique' || toBool(strval($index_info['unique']))); + $index->is_unique = toBool($index_info['unique'] ?? ''); if (isset($table->columns[$idxcolumn]) && is_object($table->columns[$idxcolumn])) { $table->columns[$idxcolumn]->is_indexed = true; @@ -197,13 +192,14 @@ class DBTableParser // Load other constraints (foreign keys). foreach ($xml->constraint as $const_info) { + $const_info = self::_getAttributes($const_info); $constraint = new DBTable\Constraint; - $constraint->type = strtolower($const_info['type']); - $constraint->column = strval($const_info['column']) ?: null; - $constraint->references = strval($const_info['references']) ?: null; - $constraint->condition = strval($const_info['condition']) ?: null; - $constraint->on_delete = (strtolower($const_info['on_delete'] ?: $const_info['on-delete'])) ?: $constraint->on_delete; - $constraint->on_update = (strtolower($const_info['on_update'] ?: $const_info['on-update'])) ?: $constraint->on_update; + $constraint->type = strtoupper($const_info['type']); + $constraint->column = $const_info['column'] ?: null; + $constraint->references = $const_info['references'] ?: null; + $constraint->condition = $const_info['condition'] ?: null; + $constraint->on_delete = $const_info['ondelete'] ?: $constraint->on_delete; + $constraint->on_update = $const_info['onupdate'] ?: $constraint->on_update; $table->constraints[] = $constraint; } From cc271ee1545af4c59329402e145a0ea2b9e991f9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 15:45:01 +0900 Subject: [PATCH 22/47] Initial implementation of DB class based on PDO MySQL driver --- common/framework/db.php | 539 +++++++++++++++++++++ common/framework/exceptions/dberror.php | 18 + common/framework/exceptions/queryerror.php | 2 +- common/legacy.php | 2 +- 4 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 common/framework/db.php create mode 100644 common/framework/exceptions/dberror.php diff --git a/common/framework/db.php b/common/framework/db.php new file mode 100644 index 000000000..1743dafb2 --- /dev/null +++ b/common/framework/db.php @@ -0,0 +1,539 @@ +_prefix = $config['prefix'] ?: $this->_prefix; + $this->_charset = $config['charset'] ?: $this->_charset; + $this->_engine = $config['engine'] ?: $this->_engine; + + // Connect to the DB. + $dsn = 'mysql:host=' . $config['host']; + $dsn .= (isset($config['port']) && $config['port'] != 3306) ? (';port=' . $config['port']) : ''; + $dsn .= ';dbname=' . $config['database']; + $dsn .= ';charset=' . $this->_charset; + $options = array( + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false, + ); + try + { + $this->_handle = new \PDO($dsn, $config['user'], $config['pass'], $options); + } + catch (\PDOException $e) + { + throw new Exceptions\DBError($e->getMessage(), $e->getCode()); + } + + // Get the DB version. + $this->db_version = $this->_handle->getAttribute(\PDO::ATTR_SERVER_VERSION); + } + + /** + * Get the raw PDO handle. + * + * @return PDO + */ + public function getHandle(): \PDO + { + return $this->_handle; + } + + /** + * Execute an XML-defined query. + * + * @param string $query_id + * @param array $args + * @param array $columns + * @param string $type + * @return \BaseObject + */ + public function executeQuery(string $query_id, $args = [], $column_list = []): \BaseObject + { + // Validate the args. + if (is_object($args)) + { + $args = get_object_vars($args); + } + if (!is_array($args)) + { + return $this->setError(-1, 'Invalid query arguments.'); + } + + // Force the column list to a numerical array. + $column_list = is_array($column_list) ? array_values($column_list) : array(); + + // Start measuring elapsed time. + $class_start_time = microtime(true); + + // Get the name of the XML file. + $parts = explode('.', $query_id); + if (count($parts) === 2) + { + array_unshift($parts, 'modules'); + } + $filename = \RX_BASEDIR . $parts[0] . '/' . $parts[1] . '/queries/' . $parts[2] . '.xml'; + if (!Storage::exists($filename)) + { + return $this->setError(-1, 'Query \'' . $query_id . '\' does not exist.'); + } + + // Parse and cache the XML file. + $cache_key = sprintf('query:%s:%d', $filename, filemtime($filename)); + $query = Cache::get($cache_key); + if (!$query) + { + $query = Parsers\DBQueryParser::loadXML($filename); + if ($query) + { + Cache::set($cache_key, $query, 0, true); + } + else + { + return $this->setError(-1, 'Query \'' . $query_id . '\' cannot be parsed.'); + } + } + + // Get the query string and parameters. + try + { + $query_string = $query->getQueryString($this->_prefix, $args, $column_list); + $query_params = $query->getQueryParams(); + } + catch (Exceptions\QueryError $e) + { + return $this->setError(-1, $e->getMessage()); + } + + // Prepare and execute the query. + try + { + $query_start_time = microtime(true); + $this->_last_stmt = $this->_query($query_string, $query_params); + $query_elapsed_time = microtime(true) - $query_start_time; + $result = $this->_fetch($this->_last_stmt); + } + catch (\PDOException $e) + { + return $this->setError(-1, $e->getMessage()); + } + + // Compose the result object. + $output = new \BaseObject; + $output->add('_query', $query_string); + $output->add('_elapsed_time', sprintf("%0.5f", $query_elapsed_time)); + $output->data = $result; + + // Compose statistics about elapsed time. + $class_elapsed_time = microtime(true) - $class_start_time; + $this->_query_time += $query_elapsed_time; + $this->_total_time += $class_elapsed_time; + + // Return the complete result. + return $output; + } + + /** + * Execute a literal query string. + * + * This method should not be public, as it starts with an underscore. + * But since there are many legacy apps that rely on it, we will leave it public. + * + * @param string $query_string + * @param array $query_params + * @return \PDOStatement + */ + public function _query(string $query_string, array $query_params = []): \PDOStatement + { + if (count($query_params)) + { + $stmt = $this->_handle->prepare($query_string); + $stmt->execute($query_params); + } + else + { + $stmt = $this->_handle->query($query_string); + } + + return $stmt; + } + + /** + * Fetch results from a query. + * + * This method should not be public, as it starts with an underscore. + * But since there are many legacy apps that rely on it, we will leave it public. + * + * @param \PDOStatement $stmt + * @param int $last_index + * @return array|object + */ + public function _fetch(\PDOStatement $stmt, int $last_index = 0) + { + $result = array(); + $index = $last_index; + $step = $last_index !== 0 ? -1 : 1; + + while ($row = $stmt->fetchObject()) + { + $result[$index] = $row; + $index += $step; + } + + $stmt->closeCursor(); + + if ($last_index === 0 && count($result) === 1) + { + return $result[0]; + } + else + { + return $result; + } + } + + /** + * Begin a transaction. + * + * @return int + */ + public function begin(): int + { + if (!$this->_handle->inTransaction()) + { + $this->_handle->beginTransaction(); + } + $this->_transaction_level++; + return $this->_transaction_level; + } + + /** + * Roll back a transaction. + * + * @return int + */ + public function rollback(): int + { + if ($this->_handle->inTransaction() && $this->_transaction_level === 1) + { + $this->_handle->rollBack(); + } + $this->_transaction_level--; + return $this->_transaction_level; + } + + /** + * Commit a transaction. + * + * @return int + */ + public function commit(): int + { + if ($this->_handle->inTransaction() && $this->_transaction_level === 1) + { + $this->_handle->commit(); + } + $this->_transaction_level--; + return $this->_transaction_level; + } + + /** + * Get the number of rows affected by the last statement. + * + * @return int + */ + public function getAffectedRows(): int + { + return $this->_last_stmt ? intval($this->_last_stmt->rowCount()) : 0; + } + + /** + * Get the auto-incremented ID generated by the last statement. + * + * @return int + */ + public function getInsertID(): int + { + return intval($this->_handle->lastInsertId()); + } + + /** + * Get the next global sequence value. + */ + public function getNextSequence() + { + $this->_query(sprintf('INSERT INTO `%ssequence` (seq) VALUES (0)', $this->_prefix)); + $sequence = $this->getInsertID(); + if($sequence % 10000 == 0) + { + $this->_query(sprintf('DELETE FROM `%ssequence` WHERE seq < %d', $this->_prefix, $sequence)); + } + return $sequence; + } + + /** + * Drop a table. + * + * @param string $table_name + * @return BaseObject + */ + public function dropTable(string $table_name): \BaseObject + { + return new \BaseObject; + } + + /** + * Check if the last statement produced an error. + * + * @return bool + */ + public function isError(): bool + { + return $this->_errno !== 0 ? true : false; + } + + /** + * Get the last error information. + * + * @return \BaseObject + */ + public function getError(): \BaseObject + { + return new \BaseObject($this->_errno, $this->_errstr); + } + + /** + * Set error information to instance properties. + * + * @param int $errno + * @param string $errstr + * @return BaseObject + */ + public function setError(int $errno = 0, string $errstr = 'success'): \BaseObject + { + $this->_errno = $errno; + $this->_errstr = $errstr; + return new \BaseObject($errno, $errstr); + } + + /** + * Send an entry to the query log for debugging. + * + * @param array $log + * @return void + */ + public function setQueryLog(array $log) + { + Debug::addQuery($log); + } + + /** + * ========================== DEPRECATED METHODS ========================== + * ==================== KEPT FOR COMPATIBILITY WITH XE ==================== + */ + + /** + * Old alias to getInstance(). + * + * @deprecated + * @return self + */ + public static function create(): self + { + return self::getInstance(); + } + + /** + * Get the list of supported database drivers. + * + * @deprecated + * @return array + */ + public static function getSupportedList(): array + { + return array( + (object)array( + 'db_type' => 'mysql', + 'enable' => extension_loaded('pdo_mysql'), + ), + ); + } + + /** + * Get the list of enabled database drivers. + * + * @deprecated + * @return array + */ + public static function getEnableList(): array + { + return array_filter(self::getSupportedList(), function($item) { + return $item->enable; + }); + } + + /** + * Get the list of disabled database drivers. + * + * @deprecated + * @return array + */ + public static function getDisableList(): array + { + return array_filter(self::getSupportedList(), function($item) { + return !$item->enable; + }); + } + + /** + * Check if the current instance is supported. + * + * @deprecated + * @return bool + */ + public function isSupported(): bool + { + return true; + } + + /** + * Check if the current instance is connected. + * + * @deprecated + * @return bool + */ + public function isConnected(): bool + { + return true; + } + + /** + * Close the DB connection. + * + * @deprecated + * @return bool + */ + public function close(): bool + { + return true; + } + + /** + * Methods related to the click count cache feature. + * + * @deprecated + * @return bool + */ + public function getCountCache(): bool + { + return false; + } + public function putCountCache(): bool + { + return false; + } + public function resetCountCache(): bool + { + return false; + } + + /** + * Other deprecated methods. + */ + public function _getSlaveConnectionStringIndex(): int + { + return 0; + } + public function _getConnection(): \PDO + { + return $this->getHandle(); + } + public function _dbInfoExists(): bool + { + return true; + } +} diff --git a/common/framework/exceptions/dberror.php b/common/framework/exceptions/dberror.php new file mode 100644 index 000000000..2d7048ef9 --- /dev/null +++ b/common/framework/exceptions/dberror.php @@ -0,0 +1,18 @@ +executeQuery($query_id, $args, $arg_columns); - if(isset($output->data) && !is_array($output->data) && countobj($output->data) > 0) + if(isset($output->data) && is_object($output->data)) { $output->data = array($output->data); } From 8f746af45f15dae1581f883315795e68827f54d0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 17:00:04 +0900 Subject: [PATCH 23/47] Implement more methods for DB class --- common/framework/db.php | 293 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 287 insertions(+), 6 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 1743dafb2..163ec0946 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -285,7 +285,16 @@ class DB { if (!$this->_handle->inTransaction()) { - $this->_handle->beginTransaction(); + try + { + $this->_handle->beginTransaction(); + $result = 'success'; + } + catch (\PDOException $e) + { + $result = 'error'; + } + $this->setQueryLog(array('query' => 'START TRANSACTION', 'result' => $result)); } $this->_transaction_level++; return $this->_transaction_level; @@ -300,7 +309,16 @@ class DB { if ($this->_handle->inTransaction() && $this->_transaction_level === 1) { - $this->_handle->rollBack(); + try + { + $this->_handle->rollBack(); + $result = 'success'; + } + catch (\PDOException $e) + { + $result = 'error'; + } + $this->setQueryLog(array('query' => 'ROLLBACK', 'result' => $result)); } $this->_transaction_level--; return $this->_transaction_level; @@ -315,7 +333,16 @@ class DB { if ($this->_handle->inTransaction() && $this->_transaction_level === 1) { - $this->_handle->commit(); + try + { + $this->_handle->commit(); + $result = 'success'; + } + catch (\PDOException $e) + { + $result = 'error'; + } + $this->setQueryLog(array('query' => 'COMMIT', 'result' => $result)); } $this->_transaction_level--; return $this->_transaction_level; @@ -355,6 +382,53 @@ class DB return $sequence; } + /** + * Check if a password is valid according to MySQL's old password hashing algorithm. + * + * @param string $password + * @param string $saved_password + * @return bool + */ + public function isValidOldPassword(string $password, string $saved_password): bool + { + $stmt = $this->_query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', array($password, $password)); + $result = $this->_fetch($stmt); + if ($result->pw1 === $saved_password || $result->pw2 === $saved_password) + { + return true; + } + else + { + return false; + } + } + + /** + * Check if a table exists. + * + * @param string $table_name + * @return bool + */ + public function isTableExists(string $table_name): bool + { + return true; + } + + /** + * Create a table. + * + * @param string $filename + * @param string $content + * @return BaseObject + */ + public function createTable(string $filename = '', string $content = ''): \BaseObject + { + $table = Parsers\DBTableParser::loadXML($filename, $content); + $query = $table->getCreateQuery($this->_prefix, $this->_charset, $this->_engine); + + return new \BaseObject; + } + /** * Drop a table. * @@ -366,6 +440,146 @@ class DB return new \BaseObject; } + /** + * Check if a column exists. + * + * @param string $table_name + * @param string $column_name + * @return bool + */ + public function isColumnExists(string $table_name, string $column_name): bool + { + return true; + } + + /** + * Add a column. + * + * @param string $table_name + * @param string $column_name + * @param string $type + * @param string $size + * @param mixed $default + * @param bool $notnull + * @param string $after_column + * @return BaseObject + */ + public function addColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false, $after_column = null): \BaseObject + { + return new \BaseObject; + } + + /** + * Modify a column. + * + * @param string $table_name + * @param string $type + * @param string $size + * @param mixed $default + * @param bool $notnull + * @return BaseObject + */ + public function modifyColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false): \BaseObject + { + return new \BaseObject; + } + + /** + * Drop a column. + * + * @param string $table_name + * @param string $column_name + * @return BaseObject + */ + public function dropColumn(string $table_name, string $column_name): \BaseObject + { + return new \BaseObject; + } + + /** + * Get column information. + * + * @param string $table_name + * @param string $column_name + * @return Parsers\DBTable\Column; + */ + public function getColumnInfo(string $table_name, string $column_name): Parsers\DBTable\Column + { + return new Parsers\DBTable\Column; + } + + /** + * Check if an index exists. + * + * @param string $table_name + * @param string $index_name + * @return boolean + */ + function isIndexExists(string $table_name, string $index_name): bool + { + return true; + } + + /** + * Add an index. + * + * @param string $table_name + * @param string $index_name + * @param array $columns + * @param bool $unique + * @return \BaseObject + */ + function addIndex(string $table_name, string $index_name, $columns, $unique = false): \BaseObject + { + if (!is_array($columns)) + { + $columns = array($columns); + } + + return new \BaseObject; + } + + /** + * Drop an index. + * + * @param string $table_name + * @param string $index_name + * @return BaseObject + */ + function dropIndex(string $table_name, string $index_name): \BaseObject + { + return new \BaseObject; + } + + /** + * Escape a string according to current DB settings. + */ + public function addQuotes($str): string + { + if (is_numeric($str)) + { + return strval($str); + } + else + { + return $this->_handle->quote($str); + } + } + + /** + * Find out the best supported character set. + * + * @return string + */ + public function getBestSupportedCharset(): string + { + $output = $this->_fetch($this->_query("SHOW CHARACTER SET LIKE 'utf8%'"), 1); + $utf8mb4_support = ($output && count(array_filter($output, function($row) { + return $row->Charset === 'utf8mb4'; + }))); + return $utf8mb4_support ? 'utf8mb4' : 'utf8'; + } + /** * Check if the last statement produced an error. * @@ -393,11 +607,17 @@ class DB * @param string $errstr * @return BaseObject */ - public function setError(int $errno = 0, string $errstr = 'success'): \BaseObject + public function setError(int $errno = 0, string $errstr = 'success', bool $page_handler = false): \BaseObject { $this->_errno = $errno; $this->_errstr = $errstr; - return new \BaseObject($errno, $errstr); + $output = new \BaseObject($errno, $errstr); + if ($page_handler) + { + + } + + return $output; } /** @@ -427,6 +647,41 @@ class DB return self::getInstance(); } + /** + * Old alias to $stmt->fetchObject(). + * + * @deprecated + * @param \PDOStatement $stmt + * @return object|false + */ + public function db_fetch_object(\PDOStatement $stmt) + { + return $stmt->fetchObject(); + } + + /** + * Old alias to $stmt->closeCursor(). + * + * @deprecated + * @param \PDOStatement $stmt + * @return bool + */ + public function db_free_result(\PDOStatement $stmt): bool + { + return $stmt->closeCursor(); + } + + /** + * Old alias to getInsertID(). + * + * @deprecated + * @return int + */ + public function db_insert_id(): int + { + return $this->getInsertID(); + } + /** * Get the list of supported database drivers. * @@ -442,7 +697,7 @@ class DB ), ); } - + /** * Get the list of enabled database drivers. * @@ -502,6 +757,28 @@ class DB return true; } + /** + * Methods related to table creation. + * + * @deprecated + * @return void + */ + public function createTableByXmlFile($filename) + { + $output = $this->createTable($filename); + return $output->toBool(); + } + public function createTableByXml($xml_doc) + { + $output = $this->createTable('', $xml_doc); + return $output->toBool(); + } + public function _createTable($xml_doc) + { + $output = $this->createTable('', $xml_doc); + return $output->toBool(); + } + /** * Methods related to the click count cache feature. * @@ -524,6 +801,10 @@ class DB /** * Other deprecated methods. */ + public function getParser(): bool + { + return false; + } public function _getSlaveConnectionStringIndex(): int { return 0; From 7e96affa1ad665d2a9449918f3fbc3152b215fc1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 20:11:04 +0900 Subject: [PATCH 24/47] Add prepare() and query() for direct querying --- common/framework/db.php | 111 +++++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 19 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 163ec0946..f85c9c10f 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -123,6 +123,53 @@ class DB return $this->_handle; } + /** + * Create a prepared statement. + * + * @param string $query_string + * @return \PDOStatement + */ + public function prepare(string $query_string): \PDOStatement + { + // Add table prefixes to the query string. + $query_string = $this->addPrefixes($query_string); + + // Create and return a prepared statement. + $stmt = $this->_handle->prepare($query_string); + return $stmt; + } + + /** + * Execute a query string. + * + * @param string $query_string + * @param mixed ...$args + * @return \PDOStatement + */ + public function query(string $query_string, ...$args): \PDOStatement + { + // If query parameters are given as a single array, unpack it. + if (count($args) === 1 && is_array($args[0])) + { + $args = $args[0]; + } + + // Add table prefixes to the query string. + $query_string = $this->addPrefixes($query_string); + + // Execute either a prepared statement or a regular query depending on whether there are arguments. + if (count($args)) + { + $stmt = $this->_handle->prepare($query_string); + $stmt->execute($args); + } + else + { + $stmt = $this->_handle->query($query_string); + } + return $stmt; + } + /** * Execute an XML-defined query. * @@ -193,9 +240,17 @@ class DB try { $query_start_time = microtime(true); - $this->_last_stmt = $this->_query($query_string, $query_params); - $query_elapsed_time = microtime(true) - $query_start_time; + if (count($query_params)) + { + $this->_last_stmt = $this->_handle->prepare($query_string); + $this->_last_stmt->execute($query_params); + } + else + { + $this->_last_stmt = $this->_handle->query($query_string); + } $result = $this->_fetch($this->_last_stmt); + $query_elapsed_time = microtime(true) - $query_start_time; } catch (\PDOException $e) { @@ -224,22 +279,12 @@ class DB * But since there are many legacy apps that rely on it, we will leave it public. * * @param string $query_string - * @param array $query_params * @return \PDOStatement */ - public function _query(string $query_string, array $query_params = []): \PDOStatement + public function _query(string $query_string): \PDOStatement { - if (count($query_params)) - { - $stmt = $this->_handle->prepare($query_string); - $stmt->execute($query_params); - } - else - { - $stmt = $this->_handle->query($query_string); - } - - return $stmt; + $this->_last_stmt = $this->_handle->query($query_string); + return $this->_last_stmt; } /** @@ -373,11 +418,11 @@ class DB */ public function getNextSequence() { - $this->_query(sprintf('INSERT INTO `%ssequence` (seq) VALUES (0)', $this->_prefix)); + $this->_handle->exec(sprintf('INSERT INTO `sequence` (seq) VALUES (0)')); $sequence = $this->getInsertID(); if($sequence % 10000 == 0) { - $this->_query(sprintf('DELETE FROM `%ssequence` WHERE seq < %d', $this->_prefix, $sequence)); + $this->_handle->exec(sprintf('DELETE FROM `sequence` WHERE seq < %d', $sequence)); } return $sequence; } @@ -391,7 +436,7 @@ class DB */ public function isValidOldPassword(string $password, string $saved_password): bool { - $stmt = $this->_query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', array($password, $password)); + $stmt = $this->query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', $password, $password); $result = $this->_fetch($stmt); if ($result->pw1 === $saved_password || $result->pw2 === $saved_password) { @@ -551,8 +596,36 @@ class DB return new \BaseObject; } + /** + * Add table prefixes to a query string. + * + * @param string $query_string + * @return string + */ + public function addPrefixes($query_string): string + { + if (!$this->_prefix) + { + return $query_string; + } + else + { + return preg_replace_callback('/(FROM|JOIN)\s+((?:`?\w+\`?)(?:\s+AS\s+`?\w+`?)?(?:\s*,\s*(?:`?\w+\`?)(?:\s+AS\s+`?\w+`?)?)*)/i', function($m) { + $tables = array_map(function($str) { + return preg_replace_callback('/`?(\w+)`?(?:\s+AS\s+`?(\w+)`?)?/i', function($m) { + return isset($m[2]) ? sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[2]) : sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[1]); + }, trim($str)); + }, explode(',', $m[2])); + return $m[1] . ' ' . implode(', ', $tables); + }, $query_string); + } + } + /** * Escape a string according to current DB settings. + * + * @param string $str + * @return string */ public function addQuotes($str): string { @@ -573,7 +646,7 @@ class DB */ public function getBestSupportedCharset(): string { - $output = $this->_fetch($this->_query("SHOW CHARACTER SET LIKE 'utf8%'"), 1); + $output = $this->_fetch($this->_handle->query("SHOW CHARACTER SET LIKE 'utf8%'"), 1); $utf8mb4_support = ($output && count(array_filter($output, function($row) { return $row->Charset === 'utf8mb4'; }))); From 84a7b915b71adde0e656927379e020057103c75d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 21:32:25 +0900 Subject: [PATCH 25/47] Implement the COUNT(*) query for pagination --- common/framework/db.php | 112 ++++++++++++++++++--- common/framework/parsers/dbquery/query.php | 21 ++++ 2 files changed, 119 insertions(+), 14 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index f85c9c10f..581443c91 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -236,6 +236,85 @@ class DB return $this->setError(-1, $e->getMessage()); } + // If this query requires pagination, execute the COUNT(*) query first. + $last_index = 0; + if ($query->requiresPagination()) + { + $output = $this->_executeCountQuery($query, $args, $last_index); + if (!$output->toBool()) + { + return $output; + } + + // Do not execute the main query if the current page is out of bounds. + if ($output->page > $output->total_page) + { + $output->add('_query', $query_string); + $output->add('_elapsed_time', '0.00000'); + $this->_total_time += (microtime(true) - $class_start_time); + return $output; + } + } + else + { + $output = new \BaseObject; + } + + // Prepare and execute the main query. + try + { + $query_start_time = microtime(true); + if (count($query_params)) + { + $this->_last_stmt = $this->_handle->prepare($query_string); + $this->_last_stmt->execute($query_params); + } + else + { + $this->_last_stmt = $this->_handle->query($query_string); + } + $result = $this->_fetch($this->_last_stmt, $last_index); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + } + catch (\PDOException $e) + { + return $this->setError(-1, $e->getMessage()); + } + + // Fill query information and result data in the output object. + $output->add('_query', $query_string); + $output->add('_elapsed_time', sprintf("%0.5f", $query_elapsed_time)); + $output->data = $result; + + // Record statistics about elapsed time. + $this->_total_time += (microtime(true) - $class_start_time); + + // Return the complete result. + return $output; + } + + /** + * Execute a COUNT(*) query for pagination. + * + * @param Parsers\DBQuery\Query $query + * @param array $args + * @param int $last_index + * @return BaseObject + */ + protected function _executeCountQuery(Parsers\DBQuery\Query $query, array $args, int &$last_index): \BaseObject + { + // Get the COUNT(*) query string and parameters. + try + { + $query_string = $query->getQueryString($this->_prefix, $args, [], true); + $query_params = $query->getQueryParams(); + } + catch (Exceptions\QueryError $e) + { + return $this->setError(-1, $e->getMessage()); + } + // Prepare and execute the query. try { @@ -251,24 +330,29 @@ class DB } $result = $this->_fetch($this->_last_stmt); $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; } catch (\PDOException $e) { return $this->setError(-1, $e->getMessage()); } - // Compose the result object. + // Collect various counts used in the page calculation. + list($is_expression, $list_count) = $query->navigation->list_count->getValue($args); + list($is_expression, $page_count) = $query->navigation->page_count->getValue($args); + list($is_expression, $page) = $query->navigation->page->getValue($args); + $total_count = intval($result->count); + $total_page = max(1, intval(ceil($total_count / $list_count))); + $last_index = $total_count - (($page - 1) * $list_count); + $page_handler = new \PageHandler($total_count, $total_page, $page, $page_count); + + // Compose the output object. $output = new \BaseObject; - $output->add('_query', $query_string); - $output->add('_elapsed_time', sprintf("%0.5f", $query_elapsed_time)); - $output->data = $result; - - // Compose statistics about elapsed time. - $class_elapsed_time = microtime(true) - $class_start_time; - $this->_query_time += $query_elapsed_time; - $this->_total_time += $class_elapsed_time; - - // Return the complete result. + $output->total_count = $total_count; + $output->total_page = $total_page; + $output->page = $page; + $output->data = null; + $output->page_navigation = $page_handler; return $output; } @@ -560,7 +644,7 @@ class DB * @param string $index_name * @return boolean */ - function isIndexExists(string $table_name, string $index_name): bool + public function isIndexExists(string $table_name, string $index_name): bool { return true; } @@ -574,7 +658,7 @@ class DB * @param bool $unique * @return \BaseObject */ - function addIndex(string $table_name, string $index_name, $columns, $unique = false): \BaseObject + public function addIndex(string $table_name, string $index_name, $columns, $unique = false): \BaseObject { if (!is_array($columns)) { @@ -591,7 +675,7 @@ class DB * @param string $index_name * @return BaseObject */ - function dropIndex(string $table_name, string $index_name): \BaseObject + public function dropIndex(string $table_name, string $index_name): \BaseObject { return new \BaseObject; } diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index 5b1493318..8d7a17d09 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -19,6 +19,7 @@ class Query extends VariableBase public $navigation = null; public $select_distinct = false; public $update_duplicate = false; + public $requires_pagination = false; /** * Attributes for subqueries in the or section. @@ -95,6 +96,16 @@ class Query extends VariableBase return $this->_params; } + /** + * Check if this query requires pagination. + * + * @return bool + */ + public function requiresPagination(): bool + { + return $this->requires_pagination; + } + /** * Generate a SELECT query string. * @@ -514,9 +525,19 @@ class Query extends VariableBase { list($is_expression, $offset) = $navigation->offset->getValue($this->_args); } + + // If page is available, set the offset and require pagination for this query. if ($page > 0) { $offset = $list_count * ($page - 1); + if ($this->type === 'SELECT') + { + $this->requires_pagination = true; + } + } + else + { + $page = 1; } // Return the LIMIT/OFFSET clause. From 421199b1969d3ee753c859156eba964c1e8b7be4 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 22:27:45 +0900 Subject: [PATCH 26/47] Improve debugging --- common/framework/db.php | 231 +++++++++++++++++++++++++++++++++------- 1 file changed, 195 insertions(+), 36 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 581443c91..ba556e613 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -20,6 +20,7 @@ class DB /** * Prefix and other connection settings. */ + protected $_type = ''; protected $_prefix = ''; protected $_charset = 'utf8mb4'; protected $_engine = 'innodb'; @@ -75,17 +76,19 @@ class DB } // Create an instance and return it. - return self::$_instances[$type] = new self($config); + return self::$_instances[$type] = new self($type, $config); } /** * Constructor. * * @param string $type + * @param array $config */ - public function __construct(array $config) + public function __construct(string $type, array $config) { // Save important config values to the instance. + $this->_type = $type; $this->_prefix = $config['prefix'] ?: $this->_prefix; $this->_charset = $config['charset'] ?: $this->_charset; $this->_engine = $config['engine'] ?: $this->_engine; @@ -106,7 +109,7 @@ class DB } catch (\PDOException $e) { - throw new Exceptions\DBError($e->getMessage(), $e->getCode()); + throw new Exceptions\DBError($e->getMessage(), $e->getCode(), $e); } // Get the DB version. @@ -155,18 +158,36 @@ class DB } // Add table prefixes to the query string. + $class_start_time = microtime(true); $query_string = $this->addPrefixes($query_string); // Execute either a prepared statement or a regular query depending on whether there are arguments. - if (count($args)) + try { - $stmt = $this->_handle->prepare($query_string); - $stmt->execute($args); + $query_start_time = microtime(true); + if (count($args)) + { + $stmt = $this->_handle->prepare($query_string); + $stmt->execute($args); + } + else + { + $stmt = $this->_handle->query($query_string); + } + $this->clearError(); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); } - else + catch (\PDOException $e) { - $stmt = $this->_handle->query($query_string); + $this->setError($e->getCode(), $e->getMessage()); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); } + + $this->_total_time += (microtime(true) - $class_start_time); return $stmt; } @@ -206,7 +227,10 @@ class DB $filename = \RX_BASEDIR . $parts[0] . '/' . $parts[1] . '/queries/' . $parts[2] . '.xml'; if (!Storage::exists($filename)) { - return $this->setError(-1, 'Query \'' . $query_id . '\' does not exist.'); + $output = $this->setError(-1, 'Query \'' . $query_id . '\' does not exist.'); + $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_total_time += (microtime(true) - $class_start_time); + return $output; } // Parse and cache the XML file. @@ -221,7 +245,10 @@ class DB } else { - return $this->setError(-1, 'Query \'' . $query_id . '\' cannot be parsed.'); + $output = $this->setError(-1, 'Query \'' . $query_id . '\' cannot be parsed.'); + $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_total_time += (microtime(true) - $class_start_time); + return $output; } } @@ -233,16 +260,21 @@ class DB } catch (Exceptions\QueryError $e) { - return $this->setError(-1, $e->getMessage()); + $output = $this->setError(-1, $e->getMessage()); + $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_total_time += (microtime(true) - $class_start_time); + return $output; } // If this query requires pagination, execute the COUNT(*) query first. $last_index = 0; if ($query->requiresPagination()) { - $output = $this->_executeCountQuery($query, $args, $last_index); + $output = $this->_executeCountQuery($query_id, $query, $args, $last_index); if (!$output->toBool()) { + $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_total_time += (microtime(true) - $class_start_time); return $output; } @@ -251,6 +283,7 @@ class DB { $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); + $output->page_navigation = new \PageHandler(0, 0, 0); $this->_total_time += (microtime(true) - $class_start_time); return $output; } @@ -276,33 +309,44 @@ class DB $result = $this->_fetch($this->_last_stmt, $last_index); $query_elapsed_time = microtime(true) - $query_start_time; $this->_query_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, $query_id, $query_elapsed_time)); } catch (\PDOException $e) { - return $this->setError(-1, $e->getMessage()); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + $output = $this->setError(-1, $e->getMessage()); + $output->add('_query', $query_string); + $output->add('_elapsed_time', '0.00000'); + $output->page_navigation = new \PageHandler(0, 0, 0); + Debug::addQuery($this->getQueryLog($query_string, $query_id, $query_elapsed_time)); + $this->_total_time += (microtime(true) - $class_start_time); + return $output; } // Fill query information and result data in the output object. $output->add('_query', $query_string); - $output->add('_elapsed_time', sprintf("%0.5f", $query_elapsed_time)); + $output->add('_elapsed_time', sprintf('%0.5f', $query_elapsed_time)); $output->data = $result; - // Record statistics about elapsed time. + // Record statistics about this query and elapsed time. $this->_total_time += (microtime(true) - $class_start_time); // Return the complete result. + $this->clearError(); return $output; } /** * Execute a COUNT(*) query for pagination. * + * @param string $query_id * @param Parsers\DBQuery\Query $query * @param array $args * @param int $last_index * @return BaseObject */ - protected function _executeCountQuery(Parsers\DBQuery\Query $query, array $args, int &$last_index): \BaseObject + protected function _executeCountQuery(string $query_id, Parsers\DBQuery\Query $query, array $args, int &$last_index): \BaseObject { // Get the COUNT(*) query string and parameters. try @@ -331,10 +375,15 @@ class DB $result = $this->_fetch($this->_last_stmt); $query_elapsed_time = microtime(true) - $query_start_time; $this->_query_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, $query_id . ' (count)', $query_elapsed_time)); } catch (\PDOException $e) { - return $this->setError(-1, $e->getMessage()); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + $output = $this->setError(-1, $e->getMessage()); + Debug::addQuery($this->getQueryLog($query_string, $query_id . ' (count)', $query_elapsed_time)); + return $output; } // Collect various counts used in the page calculation. @@ -367,7 +416,25 @@ class DB */ public function _query(string $query_string): \PDOStatement { - $this->_last_stmt = $this->_handle->query($query_string); + try + { + $query_start_time = microtime(true); + $this->_last_stmt = $this->_handle->query($query_string); + $this->clearError(); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + $this->_total_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); + } + catch (\PDOException $e) + { + $this->setError($e->getCode(), $e->getMessage()); + $query_elapsed_time = microtime(true) - $query_start_time; + $this->_query_time += $query_elapsed_time; + $this->_total_time += $query_elapsed_time; + Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); + } + return $this->_last_stmt; } @@ -417,13 +484,13 @@ class DB try { $this->_handle->beginTransaction(); - $result = 'success'; + $this->clearError(); } catch (\PDOException $e) { - $result = 'error'; + $this->setError($e->getCode(), $e->getMessage()); } - $this->setQueryLog(array('query' => 'START TRANSACTION', 'result' => $result)); + Debug::addQuery($this->getQueryLog('START TRANSACTION', '', 0)); } $this->_transaction_level++; return $this->_transaction_level; @@ -441,13 +508,13 @@ class DB try { $this->_handle->rollBack(); - $result = 'success'; + $this->clearError(); } catch (\PDOException $e) { - $result = 'error'; + $this->setError($e->getCode(), $e->getMessage()); } - $this->setQueryLog(array('query' => 'ROLLBACK', 'result' => $result)); + Debug::addQuery($this->getQueryLog('ROLLBACK', '', 0)); } $this->_transaction_level--; return $this->_transaction_level; @@ -465,13 +532,13 @@ class DB try { $this->_handle->commit(); - $result = 'success'; + $this->clearError(); } catch (\PDOException $e) { - $result = 'error'; + $this->setError($e->getCode(), $e->getMessage()); } - $this->setQueryLog(array('query' => 'COMMIT', 'result' => $result)); + Debug::addQuery($this->getQueryLog('COMMIT', '', 0)); } $this->_transaction_level--; return $this->_transaction_level; @@ -502,12 +569,29 @@ class DB */ public function getNextSequence() { - $this->_handle->exec(sprintf('INSERT INTO `sequence` (seq) VALUES (0)')); - $sequence = $this->getInsertID(); + try + { + $this->_handle->exec(sprintf('INSERT INTO `sequence` (seq) VALUES (0)')); + $sequence = $this->getInsertID(); + } + catch (\PDOException $e) + { + throw new Exceptions\DBError($e->getMessage(), $e->getCode(), $e); + } + if($sequence % 10000 == 0) { - $this->_handle->exec(sprintf('DELETE FROM `sequence` WHERE seq < %d', $sequence)); + try + { + $this->_handle->exec(sprintf('DELETE FROM `sequence` WHERE seq < %d', $sequence)); + } + catch (\PDOException $e) + { + $this->setError($e->getCode(), $e->getMessage()); + } } + + $this->clearError(); return $sequence; } @@ -520,8 +604,18 @@ class DB */ public function isValidOldPassword(string $password, string $saved_password): bool { - $stmt = $this->query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', $password, $password); - $result = $this->_fetch($stmt); + try + { + $stmt = $this->query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', $password, $password); + $result = $this->_fetch($stmt); + $this->clearError(); + } + catch (\PDOException $e) + { + $this->setError($e->getCode(), $e->getMessage()); + return false; + } + if ($result->pw1 === $saved_password || $result->pw2 === $saved_password) { return true; @@ -764,17 +858,82 @@ class DB * @param string $errstr * @return BaseObject */ - public function setError(int $errno = 0, string $errstr = 'success', bool $page_handler = false): \BaseObject + public function setError(int $errno = 0, string $errstr = 'success'): \BaseObject { $this->_errno = $errno; $this->_errstr = $errstr; $output = new \BaseObject($errno, $errstr); - if ($page_handler) + return $output; + } + + /** + * Clear error information. + * + * @return void + */ + public function clearError() + { + $this->_errno = 0; + $this->_errstr = 'success'; + } + + /** + * Generate a query log entry. + * + * @param string $query + * @param string $query_id + * @param float $elapsed_time + * @return array + */ + public function getQueryLog(string $query, string $query_id, float $elapsed_time): array + { + // Cache the debug status to improve performance. + static $debug_enabled = null; + static $debug_queries = null; + if ($debug_enabled === null) { - + $debug_enabled = Config::get('debug.enabled'); + } + if ($debug_queries === null) + { + $debug_queries = in_array('queries', Config::get('debug.display_content') ?: []); } - return $output; + // Compose the basic structure of the log entry. + $result = array( + 'query' => $query, + 'query_id' => $query_id, + 'connection' => $this->_type, + 'elapsed_time' => sprintf('%0.5f', $elapsed_time), + 'result' => 'success', + 'errno' => $this->_errno, + 'errstr' => $this->_errstr, + 'called_file' => null, + 'called_line' => null, + 'called_method' => null, + 'backtrace' => array(), + ); + + // Add debug information if enabled. + if ($debug_enabled && ($this->_errno || $debug_queries)) + { + $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); + foreach ($backtrace as $no => $call) + { + if (in_array($call['function'], array('executeQuery', 'executeQueryArray'))) + { + $no++; + $result['called_file'] = $backtrace[$no]['file']; + $result['called_line'] = $backtrace[$no]['line']; + $no++; + $result['called_method'] = $backtrace[$no]['class'] . $backtrace[$no]['type'] . $backtrace[$no]['function']; + $result['backtrace'] = array_slice($backtrace, $no, 1); + break; + } + } + } + + return $result; } /** From 902b931b89a160faf62f88d76b235566f865fa91 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 22:30:48 +0900 Subject: [PATCH 27/47] Provide methods to tell total elapsed time --- common/framework/db.php | 20 ++++++++++++++++++++ common/framework/debug.php | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index ba556e613..1443fe05c 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -947,6 +947,26 @@ class DB Debug::addQuery($log); } + /** + * Get total time spent during queries. + * + * @return float + */ + public function getQueryElapsedTime() + { + return $this->_query_time; + } + + /** + * Get total time spent in this class. + * + * @return float + */ + public function getTotalElapsedTime() + { + return $this->_total_time; + } + /** * ========================== DEPRECATED METHODS ========================== * ==================== KEPT FOR COMPATIBILITY WITH XE ==================== diff --git a/common/framework/debug.php b/common/framework/debug.php index f9ce31076..37b2646a2 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -746,6 +746,7 @@ class Debug public static function getDebugData() { // Collect debug information. + $db = DB::getInstance(); $data = (object)array( 'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', \RX_TIME), 'url' => getCurrentPageUrl(), @@ -761,8 +762,8 @@ class Debug 'total' => sprintf('%0.4f sec', microtime(true) - \RX_MICROTIME), 'template' => sprintf('%0.4f sec (count: %d)', $GLOBALS['__template_elapsed__'], $GLOBALS['__TemplateHandlerCalled__']), 'xmlparse' => sprintf('%0.4f sec', $GLOBALS['__xmlparse_elapsed__']), - 'db_query' => sprintf('%0.4f sec (count: %d)', $GLOBALS['__db_elapsed_time__'], count(self::$_queries)), - 'db_class' => sprintf('%0.4f sec', $GLOBALS['__dbclass_elapsed_time__'] - $GLOBALS['__db_elapsed_time__']), + 'db_query' => sprintf('%0.4f sec (count: %d)', $db->getQueryElapsedTime(), count(self::$_queries)), + 'db_class' => sprintf('%0.4f sec', $db->getTotalElapsedTime() - $db->getQueryElapsedTime()), 'layout' => sprintf('%0.4f sec', $GLOBALS['__layout_compile_elapsed__']), 'widget' => sprintf('%0.4f sec', $GLOBALS['__widget_excute_elapsed__']), 'remote' => sprintf('%0.4f sec', $GLOBALS['__remote_request_elapsed__']), From 1903deee405c5b425da553b49d834fdf89265674 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 22:37:48 +0900 Subject: [PATCH 28/47] Fix condition for recording backtraces --- common/framework/db.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 1443fe05c..968a32042 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -920,12 +920,10 @@ class DB $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); foreach ($backtrace as $no => $call) { - if (in_array($call['function'], array('executeQuery', 'executeQueryArray'))) + if ($call['file'] !== __FILE__ && $call['file'] !== \RX_BASEDIR . 'common/legacy.php') { - $no++; $result['called_file'] = $backtrace[$no]['file']; $result['called_line'] = $backtrace[$no]['line']; - $no++; $result['called_method'] = $backtrace[$no]['class'] . $backtrace[$no]['type'] . $backtrace[$no]['function']; $result['backtrace'] = array_slice($backtrace, $no, 1); break; From 98bf9e485cfbc6ecbfce9f5b3a9711c6504dbd00 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 22:46:38 +0900 Subject: [PATCH 29/47] Implement isTableExists() and fix return types of prepare(), query(), etc. --- common/framework/db.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 968a32042..5b2f66e47 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -132,14 +132,14 @@ class DB * @param string $query_string * @return \PDOStatement */ - public function prepare(string $query_string): \PDOStatement + public function prepare(string $query_string) { // Add table prefixes to the query string. $query_string = $this->addPrefixes($query_string); // Create and return a prepared statement. - $stmt = $this->_handle->prepare($query_string); - return $stmt; + $this->_last_stmt = $this->_handle->prepare($query_string); + return $this->_last_stmt; } /** @@ -149,7 +149,7 @@ class DB * @param mixed ...$args * @return \PDOStatement */ - public function query(string $query_string, ...$args): \PDOStatement + public function query(string $query_string, ...$args) { // If query parameters are given as a single array, unpack it. if (count($args) === 1 && is_array($args[0])) @@ -167,12 +167,12 @@ class DB $query_start_time = microtime(true); if (count($args)) { - $stmt = $this->_handle->prepare($query_string); - $stmt->execute($args); + $this->_last_stmt = $this->_handle->prepare($query_string); + $this->_last_stmt->execute($args); } else { - $stmt = $this->_handle->query($query_string); + $this->_last_stmt = $this->_handle->query($query_string); } $this->clearError(); $query_elapsed_time = microtime(true) - $query_start_time; @@ -188,7 +188,7 @@ class DB } $this->_total_time += (microtime(true) - $class_start_time); - return $stmt; + return $this->_last_stmt; } /** @@ -634,7 +634,16 @@ class DB */ public function isTableExists(string $table_name): bool { - return true; + $stmt = $this->_query(sprintf("SHOW TABLES LIKE %s", $this->addQuotes($this->_prefix . $table_name))); + $result = $this->_fetch($stmt); + if ($result) + { + return true; + } + else + { + return false; + } } /** From 4a7c18f0ac2f5389226a91567ad2a7d66d857347 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 29 Jun 2020 23:16:03 +0900 Subject: [PATCH 30/47] Implement most methods for adding/dropping tables and indexes --- common/framework/db.php | 63 ++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 5b2f66e47..2a221b79e 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -414,7 +414,7 @@ class DB * @param string $query_string * @return \PDOStatement */ - public function _query(string $query_string): \PDOStatement + public function _query(string $query_string) { try { @@ -634,16 +634,9 @@ class DB */ public function isTableExists(string $table_name): bool { - $stmt = $this->_query(sprintf("SHOW TABLES LIKE %s", $this->addQuotes($this->_prefix . $table_name))); + $stmt = $this->_query(sprintf("SHOW TABLES LIKE '%s'", $this->addQuotes($this->_prefix . $table_name))); $result = $this->_fetch($stmt); - if ($result) - { - return true; - } - else - { - return false; - } + return $result ? true : false; } /** @@ -656,9 +649,14 @@ class DB public function createTable(string $filename = '', string $content = ''): \BaseObject { $table = Parsers\DBTableParser::loadXML($filename, $content); - $query = $table->getCreateQuery($this->_prefix, $this->_charset, $this->_engine); + if (!$table) + { + return $this->setError(-1, 'Table creation failed.'); + } - return new \BaseObject; + $query_string = $table->getCreateQuery($this->_prefix, $this->_charset, $this->_engine); + $stmt = $this->_query($query_string); + return $stmt ? new \BaseObject : $this->getError(); } /** @@ -669,7 +667,8 @@ class DB */ public function dropTable(string $table_name): \BaseObject { - return new \BaseObject; + $stmt = $this->_query(sprintf("DROP TABLE `%s`", $this->addQuotes($this->_prefix . $table_name))); + return $stmt ? new \BaseObject : $this->getError(); } /** @@ -681,7 +680,9 @@ class DB */ public function isColumnExists(string $table_name, string $column_name): bool { - return true; + $stmt = $this->_query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + $result = $this->_fetch($stmt); + return $result ? true : false; } /** @@ -698,6 +699,7 @@ class DB */ public function addColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false, $after_column = null): \BaseObject { + // TODO return new \BaseObject; } @@ -713,6 +715,7 @@ class DB */ public function modifyColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false): \BaseObject { + // TODO return new \BaseObject; } @@ -725,7 +728,8 @@ class DB */ public function dropColumn(string $table_name, string $column_name): \BaseObject { - return new \BaseObject; + $stmt = $this->_query(sprintf("ALTER TABLE `%s` DROP `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + return $stmt ? new \BaseObject : $this->getError(); } /** @@ -737,6 +741,7 @@ class DB */ public function getColumnInfo(string $table_name, string $column_name): Parsers\DBTable\Column { + // TODO return new Parsers\DBTable\Column; } @@ -749,7 +754,9 @@ class DB */ public function isIndexExists(string $table_name, string $index_name): bool { - return true; + $stmt = $this->_query(sprintf("SHOW INDEX FROM `%s` WHERE Key_name = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); + $result = $this->_fetch($stmt); + return $result ? true : false; } /** @@ -768,7 +775,24 @@ class DB $columns = array($columns); } - return new \BaseObject; + $query = vsprintf("ALTER TABLE `%s` ADD %s `%s` (%s);", array( + $this->addQuotes($this->_prefix . $table_name), + $unique ? 'UNIQUE INDEX' : 'INDEX', + $this->addQuotes($index_name), + implode(', ', array_map(function($column_name) { + if (preg_match('/^([^()]+)\(([0-9]+)\)$/', $column_name, $matches)) + { + return '`' . $this->addQuotes($matches[1]) . '`(' . $matches[2] . ')'; + } + else + { + return '`' . $this->addQuotes($column_name) . '`'; + } + }, $columns)), + )); + + $stmt = $this->_query($query); + return $stmt ? new \BaseObject : $this->getError(); } /** @@ -780,7 +804,8 @@ class DB */ public function dropIndex(string $table_name, string $index_name): \BaseObject { - return new \BaseObject; + $stmt = $this->_query(sprintf("ALTER TABLE `%s` DROP INDEX `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); + return $stmt ? new \BaseObject : $this->getError(); } /** @@ -822,7 +847,7 @@ class DB } else { - return $this->_handle->quote($str); + return preg_replace("/^'(.*)'$/s", '$1', $this->_handle->quote($str)); } } From 58133573b39d5784157dfa970c3d5a9dd15ee148 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 00:31:06 +0900 Subject: [PATCH 31/47] Implement addColumn() and modifyColumn() --- common/framework/db.php | 86 ++++++++++++++++++-- common/framework/parsers/dbtableparser.php | 95 +++++++++++++++------- 2 files changed, 148 insertions(+), 33 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 2a221b79e..60705b3e2 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -648,12 +648,14 @@ class DB */ public function createTable(string $filename = '', string $content = ''): \BaseObject { + // Get the table definition from DBTableParser. $table = Parsers\DBTableParser::loadXML($filename, $content); if (!$table) { return $this->setError(-1, 'Table creation failed.'); } + // Generate the CREATE TABLE query and execute it. $query_string = $table->getCreateQuery($this->_prefix, $this->_charset, $this->_engine); $stmt = $this->_query($query_string); return $stmt ? new \BaseObject : $this->getError(); @@ -699,24 +701,98 @@ class DB */ public function addColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false, $after_column = null): \BaseObject { - // TODO - return new \BaseObject; + // Normalize the type and size. + list($type, $xetype, $size) = Parsers\DBTableParser::getTypeAndSize($type, strval($size)); + + // Compose the ADD COLUMN query. + $query = sprintf("ALTER TABLE `%s` ADD COLUMN `%s` ", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name)); + $query .= $size ? sprintf('%s(%s)', $type, $size) : $type; + $query .= $notnull ? ' NOT NULL' : ''; + + // Add the default value according to the type. + if (isset($default)) + { + if (contains('int', $type, false) && is_numeric($default)) + { + $query .= sprintf(" DEFAULT %s", $default); + } + else + { + $query .= sprintf(" DEFAULT '%s'", $this->addQuotes($default)); + } + } + + // Add position information. + if ($after_column === 'FIRST') + { + $query .= ' FIRST'; + } + elseif ($after_column) + { + $query .= sprintf(' AFTER `%s`', $this->addQuotes($after_column)); + } + + // Execute the query and return the result. + $stmt = $this->_query($query); + return $stmt ? new \BaseObject : $this->getError(); } /** * Modify a column. * * @param string $table_name + * @param string $column_name * @param string $type * @param string $size * @param mixed $default * @param bool $notnull + * @param string $after_column + * @param string $new_name * @return BaseObject */ - public function modifyColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false): \BaseObject + public function modifyColumn(string $table_name, string $column_name, string $type = 'number', $size = null, $default = null, $notnull = false, $after_column = null, $new_name = null): \BaseObject { - // TODO - return new \BaseObject; + // Normalize the type and size. + list($type, $xetype, $size) = Parsers\DBTableParser::getTypeAndSize($type, strval($size)); + + // Compose the MODIFY COLUMN query. + if ($new_name && $new_name !== $column_name) + { + $query = sprintf("ALTER TABLE `%s` CHANGE `%s` `%s` ", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name), $this->addQuotes($new_name)); + } + else + { + $query = sprintf("ALTER TABLE `%s` MODIFY `%s` ", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name)); + } + $query .= $size ? sprintf('%s(%s)', $type, $size) : $type; + $query .= $notnull ? ' NOT NULL' : ''; + + // Add the default value according to the type. + if (isset($default)) + { + if (contains('int', $type, false) && is_numeric($default)) + { + $query .= sprintf(" DEFAULT %s", $default); + } + else + { + $query .= sprintf(" DEFAULT '%s'", $this->addQuotes($default)); + } + } + + // Add position information. + if ($after_column === 'FIRST') + { + $query .= ' FIRST'; + } + elseif ($after_column) + { + $query .= sprintf(' AFTER `%s`', $this->addQuotes($after_column)); + } + + // Execute the query and return the result. + $stmt = $this->_query($query); + return $stmt ? new \BaseObject : $this->getError(); } /** diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index 71475e09b..f91f6a1e8 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -23,6 +23,7 @@ class DBTableParser extends BaseParser protected static $_nosize_types = array( 'bigint' => true, 'int' => true, + 'integer' => true, ); /** @@ -66,34 +67,7 @@ class DBTableParser extends BaseParser // Get the column name and type. $column = new DBTable\Column; $column->name = strval($column_info['name']); - $column->type = strval($column_info['type']); - - // Map XE-compatible types to database native types. - if (isset(self::$_xe_types[$column->type])) - { - $column->xetype = $column->type; - $column->type = self::$_xe_types[$column->type]; - } - else - { - $column->xetype = $column->type; - } - - // Get the size. - if (preg_match('/^([a-z0-9_]+)\(([0-9,\s]+)\)$/i', $column->type, $matches)) - { - $column->type = $matches[1]; - $column->size = $matches[2]; - } - if (isset($column_info['size'])) - { - $column->size = strval($column_info['size']); - } - $column->size = implode(',', array_map('trim', explode(',', $column->size))) ?: null; - if (isset(self::$_nosize_types[$column->type])) - { - $column->size = null; - } + list($column->type, $column->xetype, $column->size) = self::getTypeAndSize(strval($column_info['type']), strval($column_info['size'])); // Get all attributes. $attribs = self::_getAttributes($column_info); @@ -206,4 +180,69 @@ class DBTableParser extends BaseParser // Return the complete table definition. return $table; } + + /** + * Get column type and size. + * + * @param string $type + * @param string $size + * @return array + */ + public static function getTypeAndSize(string $type, string $size): array + { + // Map XE-compatible types to database native types. + if (isset(self::$_xe_types[$type])) + { + $xetype = $type; + $type = self::$_xe_types[$type]; + } + else + { + $xetype = $type; + } + + // Extract and normalize the size. + if (preg_match('/^([a-z0-9_]+)\(([0-9,\s]+)\)$/i', $type, $matches)) + { + $type = $matches[1]; + $size = $matches[2]; + } + $size = implode(',', array_map('trim', explode(',', $size))) ?: null; + if (isset(self::$_nosize_types[$type])) + { + $size = null; + } + + // Return a complete array. + return [$type, $xetype, $size]; + } + + /** + * Get the XE-compatible type from a real database type. + * + * @param string $type + * @param string $size + * @return string + */ + public static function getXEType(string $type, string $size): string + { + switch ($type) + { + case 'bigint': + return 'bignumber'; + case 'int': + case 'integer': + return 'number'; + case 'longtext': + return 'bigtext'; + case 'char': + case 'varchar': + if ($size == 14) + { + return 'date'; + } + default: + return $type; + } + } } From 47bb30c5357d7800d1ff44898a0f660b2222d6fc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 00:40:10 +0900 Subject: [PATCH 32/47] Implement getColumnInfo() --- common/framework/db.php | 32 +++++++++++++++++++--- common/framework/parsers/dbtableparser.php | 1 + 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 60705b3e2..aee81206f 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -813,12 +813,36 @@ class DB * * @param string $table_name * @param string $column_name - * @return Parsers\DBTable\Column; + * @return object */ - public function getColumnInfo(string $table_name, string $column_name): Parsers\DBTable\Column + public function getColumnInfo(string $table_name, string $column_name) { - // TODO - return new Parsers\DBTable\Column; + // If column information is not found, return false. + $stmt = $this->_query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + $column_info = $this->_fetch($stmt); + if (!$column_info) + { + return false; + } + + // Reorganize the type information. + $dbtype = strtolower($column_info->{'Type'}); + if (preg_match('/^([a-z0-9_]+)\(([0-9,\s]+)\)$/i', $dbtype, $matches)) + { + $dbtype = $matches[1]; + $size = $matches[2]; + } + $xetype = Parsers\DBTableParser::getXEType($dbtype, $size ?: ''); + + // Return the result as an object. + return (object)array( + 'name' => $column_name, + 'dbtype' => $dbtype, + 'xetype' => $xetype, + 'size' => $size, + 'default_value' => $column_info->{'Default'}, + 'notnull' => strncmp($column_info->{'Null'}, 'NO', 2) == 0 ? true : false, + ); } /** diff --git a/common/framework/parsers/dbtableparser.php b/common/framework/parsers/dbtableparser.php index f91f6a1e8..1d1b395b5 100644 --- a/common/framework/parsers/dbtableparser.php +++ b/common/framework/parsers/dbtableparser.php @@ -226,6 +226,7 @@ class DBTableParser extends BaseParser */ public static function getXEType(string $type, string $size): string { + $type = strtolower($type); switch ($type) { case 'bigint': From 936568a8a5c50a215dee24199d44860ad0f029a1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 12:00:42 +0900 Subject: [PATCH 33/47] Improve error handling by using wrapper classes for PDO and PDOStatement --- common/framework/db.php | 219 +++++++++------------- common/framework/helpers/dbhelper.php | 116 ++++++++++++ common/framework/helpers/dbstmthelper.php | 59 ++++++ 3 files changed, 265 insertions(+), 129 deletions(-) create mode 100644 common/framework/helpers/dbhelper.php create mode 100644 common/framework/helpers/dbstmthelper.php diff --git a/common/framework/db.php b/common/framework/db.php index aee81206f..759b826f0 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -101,15 +101,17 @@ class DB $options = array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_STATEMENT_CLASS => array('\Rhymix\Framework\Helpers\DBStmtHelper'), \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false, ); try { - $this->_handle = new \PDO($dsn, $config['user'], $config['pass'], $options); + $this->_handle = new Helpers\DBHelper($dsn, $config['user'], $config['pass'], $options); + $this->_handle->setType($type); } catch (\PDOException $e) { - throw new Exceptions\DBError($e->getMessage(), $e->getCode(), $e); + throw new Exceptions\DBError($e->getMessage(), 0, $e); } // Get the DB version. @@ -117,11 +119,11 @@ class DB } /** - * Get the raw PDO handle. + * Get the PDO handle for direct manipulation. * - * @return PDO + * @return Helpers\DBHelper */ - public function getHandle(): \PDO + public function getHandle(): Helpers\DBHelper { return $this->_handle; } @@ -129,25 +131,37 @@ class DB /** * Create a prepared statement. * - * @param string $query_string - * @return \PDOStatement + * Table names in the FROM or JOIN clause of the statement are + * automatically prefixed with the configured prefix. + * + * @param string $statement + * @param array $driver_options + * @return Helpers\DBStmtHelper */ - public function prepare(string $query_string) + public function prepare(string $statement, array $driver_options = []) { // Add table prefixes to the query string. - $query_string = $this->addPrefixes($query_string); + $statement = $this->addPrefixes($statement); // Create and return a prepared statement. - $this->_last_stmt = $this->_handle->prepare($query_string); + $this->_last_stmt = $this->_handle->prepare($statement, $driver_options); return $this->_last_stmt; } /** - * Execute a query string. + * Execute a query string with or without parameters. + * + * This method will automatically use prepared statements if there are + * any parameters. It is strongly recommended to pass any user-supplied + * values as separate parameters instead of embedding them directly + * in the query string, in order to prevent SQL injection attacks. + * + * Table names in the FROM or JOIN clause of the statement are + * automatically prefixed with the configured prefix. * * @param string $query_string * @param mixed ...$args - * @return \PDOStatement + * @return Helpers\DBStmtHelper */ public function query(string $query_string, ...$args) { @@ -158,36 +172,18 @@ class DB } // Add table prefixes to the query string. - $class_start_time = microtime(true); $query_string = $this->addPrefixes($query_string); // Execute either a prepared statement or a regular query depending on whether there are arguments. - try + if (count($args)) { - $query_start_time = microtime(true); - if (count($args)) - { - $this->_last_stmt = $this->_handle->prepare($query_string); - $this->_last_stmt->execute($args); - } - else - { - $this->_last_stmt = $this->_handle->query($query_string); - } - $this->clearError(); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); + $this->_last_stmt = $this->_handle->prepare($query_string); + $this->_last_stmt->execute($args); } - catch (\PDOException $e) + else { - $this->setError($e->getCode(), $e->getMessage()); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); + $this->_last_stmt = $this->_handle->query($query_string); } - - $this->_total_time += (microtime(true) - $class_start_time); return $this->_last_stmt; } @@ -207,6 +203,10 @@ class DB { $args = get_object_vars($args); } + if (is_null($args)) + { + $args = array(); + } if (!is_array($args)) { return $this->setError(-1, 'Invalid query arguments.'); @@ -216,7 +216,7 @@ class DB $column_list = is_array($column_list) ? array_values($column_list) : array(); // Start measuring elapsed time. - $class_start_time = microtime(true); + $start_time = microtime(true); // Get the name of the XML file. $parts = explode('.', $query_id); @@ -229,7 +229,7 @@ class DB { $output = $this->setError(-1, 'Query \'' . $query_id . '\' does not exist.'); $output->page_navigation = new \PageHandler(0, 0, 0); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -247,7 +247,7 @@ class DB { $output = $this->setError(-1, 'Query \'' . $query_id . '\' cannot be parsed.'); $output->page_navigation = new \PageHandler(0, 0, 0); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } } @@ -262,7 +262,7 @@ class DB { $output = $this->setError(-1, $e->getMessage()); $output->page_navigation = new \PageHandler(0, 0, 0); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -274,7 +274,7 @@ class DB if (!$output->toBool()) { $output->page_navigation = new \PageHandler(0, 0, 0); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -284,7 +284,7 @@ class DB $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); $output->page_navigation = new \PageHandler(0, 0, 0); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } } @@ -296,7 +296,6 @@ class DB // Prepare and execute the main query. try { - $query_start_time = microtime(true); if (count($query_params)) { $this->_last_stmt = $this->_handle->prepare($query_string); @@ -307,31 +306,22 @@ class DB $this->_last_stmt = $this->_handle->query($query_string); } $result = $this->_fetch($this->_last_stmt, $last_index); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, $query_id, $query_elapsed_time)); } - catch (\PDOException $e) + catch (Exceptions\DBError $e) { - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - $output = $this->setError(-1, $e->getMessage()); $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); $output->page_navigation = new \PageHandler(0, 0, 0); - Debug::addQuery($this->getQueryLog($query_string, $query_id, $query_elapsed_time)); - $this->_total_time += (microtime(true) - $class_start_time); + $this->_total_time += (microtime(true) - $start_time); return $output; } // Fill query information and result data in the output object. + $this->_total_time += ($elapsed_time = microtime(true) - $start_time); $output->add('_query', $query_string); - $output->add('_elapsed_time', sprintf('%0.5f', $query_elapsed_time)); + $output->add('_elapsed_time', sprintf('%0.5f', $elapsed_time)); $output->data = $result; - // Record statistics about this query and elapsed time. - $this->_total_time += (microtime(true) - $class_start_time); - // Return the complete result. $this->clearError(); return $output; @@ -362,7 +352,6 @@ class DB // Prepare and execute the query. try { - $query_start_time = microtime(true); if (count($query_params)) { $this->_last_stmt = $this->_handle->prepare($query_string); @@ -373,16 +362,10 @@ class DB $this->_last_stmt = $this->_handle->query($query_string); } $result = $this->_fetch($this->_last_stmt); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, $query_id . ' (count)', $query_elapsed_time)); } - catch (\PDOException $e) + catch (Exceptions\DBError $e) { - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; $output = $this->setError(-1, $e->getMessage()); - Debug::addQuery($this->getQueryLog($query_string, $query_id . ' (count)', $query_elapsed_time)); return $output; } @@ -412,29 +395,11 @@ class DB * But since there are many legacy apps that rely on it, we will leave it public. * * @param string $query_string - * @return \PDOStatement + * @return Helpers\DBStmtHelper */ public function _query(string $query_string) { - try - { - $query_start_time = microtime(true); - $this->_last_stmt = $this->_handle->query($query_string); - $this->clearError(); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - $this->_total_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); - } - catch (\PDOException $e) - { - $this->setError($e->getCode(), $e->getMessage()); - $query_elapsed_time = microtime(true) - $query_start_time; - $this->_query_time += $query_elapsed_time; - $this->_total_time += $query_elapsed_time; - Debug::addQuery($this->getQueryLog($query_string, '', $query_elapsed_time)); - } - + $this->_last_stmt = $this->_handle->query($query_string); return $this->_last_stmt; } @@ -488,7 +453,7 @@ class DB } catch (\PDOException $e) { - $this->setError($e->getCode(), $e->getMessage()); + $this->setError(-1, $e->getMessage()); } Debug::addQuery($this->getQueryLog('START TRANSACTION', '', 0)); } @@ -512,7 +477,7 @@ class DB } catch (\PDOException $e) { - $this->setError($e->getCode(), $e->getMessage()); + $this->setError(-1, $e->getMessage()); } Debug::addQuery($this->getQueryLog('ROLLBACK', '', 0)); } @@ -536,7 +501,7 @@ class DB } catch (\PDOException $e) { - $this->setError($e->getCode(), $e->getMessage()); + $this->setError(-1, $e->getMessage()); } Debug::addQuery($this->getQueryLog('COMMIT', '', 0)); } @@ -569,26 +534,16 @@ class DB */ public function getNextSequence() { - try + $this->_handle->exec(sprintf('INSERT INTO `sequence` (seq) VALUES (0)')); + $sequence = $this->getInsertID(); + if ($this->isError()) { - $this->_handle->exec(sprintf('INSERT INTO `sequence` (seq) VALUES (0)')); - $sequence = $this->getInsertID(); - } - catch (\PDOException $e) - { - throw new Exceptions\DBError($e->getMessage(), $e->getCode(), $e); + throw new Exceptions\DBError($this->getError()->getMessage()); } if($sequence % 10000 == 0) { - try - { - $this->_handle->exec(sprintf('DELETE FROM `sequence` WHERE seq < %d', $sequence)); - } - catch (\PDOException $e) - { - $this->setError($e->getCode(), $e->getMessage()); - } + $this->_handle->exec(sprintf('DELETE FROM `sequence` WHERE seq < %d', $sequence)); } $this->clearError(); @@ -604,15 +559,10 @@ class DB */ public function isValidOldPassword(string $password, string $saved_password): bool { - try + $stmt = $this->_handle->query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', $password, $password); + $result = $this->_fetch($stmt); + if ($this->isError() || !$result) { - $stmt = $this->query('SELECT' . ' ' . 'PASSWORD(?) AS pw1, OLD_PASSWORD(?) AS pw2', $password, $password); - $result = $this->_fetch($stmt); - $this->clearError(); - } - catch (\PDOException $e) - { - $this->setError($e->getCode(), $e->getMessage()); return false; } @@ -634,7 +584,7 @@ class DB */ public function isTableExists(string $table_name): bool { - $stmt = $this->_query(sprintf("SHOW TABLES LIKE '%s'", $this->addQuotes($this->_prefix . $table_name))); + $stmt = $this->_handle->query(sprintf("SHOW TABLES LIKE '%s'", $this->addQuotes($this->_prefix . $table_name))); $result = $this->_fetch($stmt); return $result ? true : false; } @@ -657,8 +607,8 @@ class DB // Generate the CREATE TABLE query and execute it. $query_string = $table->getCreateQuery($this->_prefix, $this->_charset, $this->_engine); - $stmt = $this->_query($query_string); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec($query_string); + return $result ? new \BaseObject : $this->getError(); } /** @@ -669,7 +619,7 @@ class DB */ public function dropTable(string $table_name): \BaseObject { - $stmt = $this->_query(sprintf("DROP TABLE `%s`", $this->addQuotes($this->_prefix . $table_name))); + $stmt = $this->_handle->exec(sprintf("DROP TABLE `%s`", $this->addQuotes($this->_prefix . $table_name))); return $stmt ? new \BaseObject : $this->getError(); } @@ -682,7 +632,7 @@ class DB */ public function isColumnExists(string $table_name, string $column_name): bool { - $stmt = $this->_query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + $stmt = $this->_handle->query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); $result = $this->_fetch($stmt); return $result ? true : false; } @@ -733,8 +683,8 @@ class DB } // Execute the query and return the result. - $stmt = $this->_query($query); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec($query); + return $result ? new \BaseObject : $this->getError(); } /** @@ -791,8 +741,8 @@ class DB } // Execute the query and return the result. - $stmt = $this->_query($query); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec($query); + return $result ? new \BaseObject : $this->getError(); } /** @@ -804,8 +754,8 @@ class DB */ public function dropColumn(string $table_name, string $column_name): \BaseObject { - $stmt = $this->_query(sprintf("ALTER TABLE `%s` DROP `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec(sprintf("ALTER TABLE `%s` DROP `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + return $result ? new \BaseObject : $this->getError(); } /** @@ -818,7 +768,7 @@ class DB public function getColumnInfo(string $table_name, string $column_name) { // If column information is not found, return false. - $stmt = $this->_query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + $stmt = $this->_handle->query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); $column_info = $this->_fetch($stmt); if (!$column_info) { @@ -854,7 +804,7 @@ class DB */ public function isIndexExists(string $table_name, string $index_name): bool { - $stmt = $this->_query(sprintf("SHOW INDEX FROM `%s` WHERE Key_name = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); + $stmt = $this->_handle->query(sprintf("SHOW INDEX FROM `%s` WHERE Key_name = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); $result = $this->_fetch($stmt); return $result ? true : false; } @@ -891,8 +841,8 @@ class DB }, $columns)), )); - $stmt = $this->_query($query); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec($query); + return $result ? new \BaseObject : $this->getError(); } /** @@ -904,8 +854,8 @@ class DB */ public function dropIndex(string $table_name, string $index_name): \BaseObject { - $stmt = $this->_query(sprintf("ALTER TABLE `%s` DROP INDEX `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); - return $stmt ? new \BaseObject : $this->getError(); + $result = $this->_handle->exec(sprintf("ALTER TABLE `%s` DROP INDEX `%s`", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name))); + return $result ? new \BaseObject : $this->getError(); } /** @@ -1054,7 +1004,7 @@ class DB $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); foreach ($backtrace as $no => $call) { - if ($call['file'] !== __FILE__ && $call['file'] !== \RX_BASEDIR . 'common/legacy.php') + if (!preg_match('#/common/framework/(?:db|helpers)\b#', $call['file']) && $call['file'] !== \RX_BASEDIR . 'common/legacy.php') { $result['called_file'] = $backtrace[$no]['file']; $result['called_line'] = $backtrace[$no]['line']; @@ -1079,12 +1029,23 @@ class DB Debug::addQuery($log); } + /** + * Add elapsed time. + * + * @param float $elapsed_time + * @return void + */ + public function addElapsedTime(float $elapsed_time) + { + $this->_query_time += $elapsed_time; + } + /** * Get total time spent during queries. * * @return float */ - public function getQueryElapsedTime() + public function getQueryElapsedTime(): float { return $this->_query_time; } @@ -1094,7 +1055,7 @@ class DB * * @return float */ - public function getTotalElapsedTime() + public function getTotalElapsedTime(): float { return $this->_total_time; } diff --git a/common/framework/helpers/dbhelper.php b/common/framework/helpers/dbhelper.php new file mode 100644 index 000000000..aea3e6edd --- /dev/null +++ b/common/framework/helpers/dbhelper.php @@ -0,0 +1,116 @@ +_type = $type; + } + + /** + * Create a prepared statement. + * + * @param string $statement + * @param array $driver_options + * @return PDOStatement|DBStmtHelper + */ + public function prepare($statement, $driver_options = null) + { + try + { + if ($driver_options) + { + $stmt = parent::prepare($statement, $driver_options); + } + else + { + $stmt = parent::prepare($statement); + } + $stmt->setFetchMode(\PDO::FETCH_OBJ); + $stmt->setType($this->_type); + } + catch (\PDOException $e) + { + throw new DBError($e->getMessage(), 0, $e); + } + + return $stmt; + } + + /** + * Execute a query. + * + * @param string $statement + * @return PDOStatement|DBStmtHelper + */ + public function query($statement) + { + $start_time = microtime(true); + $db_class = DB::getInstance($this->_type); + $args = func_get_args(); + array_shift($args); + + try + { + $stmt = parent::query($statement, ...$args); + $stmt->setFetchMode(\PDO::FETCH_OBJ); + $stmt->setType($this->_type); + $db_class->clearError(); + } + catch (\PDOException $e) + { + $db_class->setError(-1, $e->getMessage()); + } + + $elapsed_time = microtime(true) - $start_time; + $db_class->addElapsedTime($elapsed_time); + Debug::addQuery($db_class->getQueryLog($statement, '', $elapsed_time)); + + return $stmt; + } + + /** + * Execute a query and return the number of affected rows. + * + * @param string $statement + * @return bool + */ + public function exec($query) + { + $start_time = microtime(true); + $db_class = DB::getInstance($this->_type); + + try + { + $result = parent::exec($query); + $db_class->clearError(); + } + catch (\PDOException $e) + { + $db_class->setError(-1, $e->getMessage()); + } + + $elapsed_time = microtime(true) - $start_time; + $db_class->addElapsedTime($elapsed_time); + Debug::addQuery($db_class->getQueryLog($query, '', $elapsed_time)); + + return $result; + } +} diff --git a/common/framework/helpers/dbstmthelper.php b/common/framework/helpers/dbstmthelper.php new file mode 100644 index 000000000..3f6a8e79d --- /dev/null +++ b/common/framework/helpers/dbstmthelper.php @@ -0,0 +1,59 @@ +_type = $type; + } + + /** + * Execute a prepared statement. + * + * @param array $input_parameters + * @return bool + */ + public function execute($input_parameters = null): bool + { + $start_time = microtime(true); + $db_class = DB::getInstance($this->_type); + + try + { + $result = parent::execute($input_parameters); + $db_class->clearError(); + + $elapsed_time = microtime(true) - $start_time; + $db_class->addElapsedTime($elapsed_time); + Debug::addQuery($db_class->getQueryLog($this->queryString, '', $elapsed_time)); + } + catch (\PDOException $e) + { + $db_class->setError(-1, $e->getMessage()); + + $elapsed_time = microtime(true) - $start_time; + $db_class->addElapsedTime($elapsed_time); + Debug::addQuery($db_class->getQueryLog($this->queryString, '', $elapsed_time)); + throw new DBError($e->getMessage(), 0, $e); + } + + return $result; + } +} From 978d3d167a02ade427d006a9e71ea319b9cec58c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 12:16:59 +0900 Subject: [PATCH 34/47] Fix error handling issues --- common/framework/db.php | 25 +++++++++++++++++-- .../parsers/dbquery/variablebase.php | 2 +- common/framework/parsers/dbqueryparser.php | 6 ++--- common/framework/parsers/moduleinfoparser.php | 16 +++++++----- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index 759b826f0..cc02dad91 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -305,7 +305,20 @@ class DB { $this->_last_stmt = $this->_handle->query($query_string); } - $result = $this->_fetch($this->_last_stmt, $last_index); + + if ($this->isError()) + { + $output = $this->getError(); + $output->add('_query', $query_string); + $output->add('_elapsed_time', '0.00000'); + $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_total_time += (microtime(true) - $start_time); + return $output; + } + else + { + $result = $this->_fetch($this->_last_stmt, $last_index); + } } catch (Exceptions\DBError $e) { @@ -361,7 +374,15 @@ class DB { $this->_last_stmt = $this->_handle->query($query_string); } - $result = $this->_fetch($this->_last_stmt); + + if ($this->isError()) + { + return $this->getError(); + } + else + { + $result = $this->_fetch($this->_last_stmt); + } } catch (Exceptions\DBError $e) { diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index b81251456..975d9fc85 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -366,7 +366,7 @@ class VariableBase } // Check minimum and maximum lengths. - $length = iconv_strlen($value, 'UTF-8'); + $length = is_scalar($value) ? iconv_strlen($value, 'UTF-8') : (is_countable($value) ? count($value) : 1); if (isset($this->minlength) && $this->minlength > 0 && $length < $this->minlength) { throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' must contain no less than ' . $this->minlength . ' characters'); diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index cc0dcac66..e30332f5b 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -54,7 +54,7 @@ class DBQueryParser extends BaseParser $query->pipe = strtoupper($attribs['pipe']) ?: 'AND'; // Load tables. - foreach ($xml->tables->table as $tag) + foreach ($xml->tables->table ?: [] as $tag) { if (trim($tag['query']) === 'true') { @@ -81,7 +81,7 @@ class DBQueryParser extends BaseParser } // Load columns. - foreach ($xml->columns->column as $tag) + foreach ($xml->columns->column ?: [] as $tag) { if ($tag->getName() === 'query') { @@ -146,7 +146,7 @@ class DBQueryParser extends BaseParser if ($xml->navigation) { $query->navigation = new DBQuery\Navigation; - foreach ($xml->navigation->index as $tag) + foreach ($xml->navigation->index ?: [] as $tag) { $orderby = new DBQuery\OrderBy; $orderby->var = trim($tag['var']) ?: null; diff --git a/common/framework/parsers/moduleinfoparser.php b/common/framework/parsers/moduleinfoparser.php index 889d08bca..954e2b079 100644 --- a/common/framework/parsers/moduleinfoparser.php +++ b/common/framework/parsers/moduleinfoparser.php @@ -78,12 +78,16 @@ class ModuleInfoParser extends BaseParser } // Add information about actions. - $action_info = ModuleActionParser::loadXML(strtr($filename, ['info.xml' => 'module.xml'])); - $info->admin_index_act = $action_info->admin_index_act; - $info->default_index_act = $action_info->default_index_act; - $info->setup_index_act = $action_info->setup_index_act; - $info->simple_setup_index_act = $action_info->simple_setup_index_act; - $info->error_handlers = $action_info->error_handlers ?: []; + $action_filename = strtr($filename, ['info.xml' => 'module.xml']); + if (file_exists($action_filename)) + { + $action_info = ModuleActionParser::loadXML($action_filename); + $info->admin_index_act = $action_info->admin_index_act; + $info->default_index_act = $action_info->default_index_act; + $info->setup_index_act = $action_info->setup_index_act; + $info->simple_setup_index_act = $action_info->simple_setup_index_act; + $info->error_handlers = $action_info->error_handlers ?: []; + } // Return the complete result. return $info; From ad5169bc7adad79671d9d7e7196873b52f9f97e9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 12:20:12 +0900 Subject: [PATCH 35/47] Remove legacy DB classes and XML query parser classes --- classes/db/DB.class.php | 1226 +---------------- classes/db/DBMysql.class.php | 1171 ---------------- classes/db/queryparts/Query.class.php | 749 ---------- classes/db/queryparts/Subquery.class.php | 80 -- .../queryparts/condition/Condition.class.php | 277 ---- .../condition/ConditionGroup.class.php | 133 -- .../condition/ConditionSubquery.class.php | 28 - .../condition/ConditionWithArgument.class.php | 99 -- .../ConditionWithoutArgument.class.php | 40 - .../expression/ClickCountExpression.class.php | 62 - .../expression/DeleteExpression.class.php | 63 - .../expression/Expression.class.php | 56 - .../expression/InsertExpression.class.php | 74 - .../expression/SelectExpression.class.php | 70 - .../expression/StarExpression.class.php | 37 - .../expression/UpdateExpression.class.php | 119 -- .../UpdateExpressionWithoutArgument.class.php | 74 - classes/db/queryparts/limit/Limit.class.php | 101 -- .../queryparts/order/OrderByColumn.class.php | 74 - .../db/queryparts/table/IndexHint.class.php | 48 - .../db/queryparts/table/JoinTable.class.php | 78 -- .../table/MysqlTableWithHint.class.php | 83 -- classes/db/queryparts/table/Table.class.php | 59 - classes/xml/XmlQueryParser.class.php | 114 -- classes/xml/xmlquery/DBParser.class.php | 333 ----- classes/xml/xmlquery/QueryParser.class.php | 127 -- .../xml/xmlquery/argument/Argument.class.php | 396 ------ .../argument/ConditionArgument.class.php | 153 -- .../xmlquery/argument/SortArgument.class.php | 20 - .../queryargument/DefaultValue.class.php | 177 --- .../queryargument/QueryArgument.class.php | 191 --- .../queryargument/SortQueryArgument.class.php | 34 - .../QueryArgumentValidator.class.php | 135 -- .../xmlquery/tags/column/ColumnTag.class.php | 35 - .../tags/column/InsertColumnTag.class.php | 62 - .../InsertColumnTagWithoutArgument.class.php | 49 - .../tags/column/InsertColumnsTag.class.php | 92 -- .../tags/column/SelectColumnTag.class.php | 84 -- .../tags/column/SelectColumnsTag.class.php | 117 -- .../tags/column/UpdateColumnTag.class.php | 108 -- .../tags/column/UpdateColumnsTag.class.php | 83 -- .../condition/ConditionGroupTag.class.php | 99 -- .../tags/condition/ConditionTag.class.php | 187 --- .../tags/condition/ConditionsTag.class.php | 102 -- .../condition/JoinConditionsTag.class.php | 27 - .../xmlquery/tags/group/GroupsTag.class.php | 66 - .../tags/navigation/IndexTag.class.php | 95 -- .../tags/navigation/LimitTag.class.php | 98 -- .../tags/navigation/NavigationTag.class.php | 137 -- .../xmlquery/tags/query/QueryTag.class.php | 407 ------ .../tags/table/HintTableTag.class.php | 66 - .../xmlquery/tags/table/TableTag.class.php | 145 -- .../xmlquery/tags/table/TablesTag.class.php | 122 -- common/autoload.php | 51 - common/legacy.php | 11 +- 55 files changed, 7 insertions(+), 8717 deletions(-) delete mode 100644 classes/db/DBMysql.class.php delete mode 100644 classes/db/queryparts/Query.class.php delete mode 100644 classes/db/queryparts/Subquery.class.php delete mode 100644 classes/db/queryparts/condition/Condition.class.php delete mode 100644 classes/db/queryparts/condition/ConditionGroup.class.php delete mode 100644 classes/db/queryparts/condition/ConditionSubquery.class.php delete mode 100644 classes/db/queryparts/condition/ConditionWithArgument.class.php delete mode 100644 classes/db/queryparts/condition/ConditionWithoutArgument.class.php delete mode 100644 classes/db/queryparts/expression/ClickCountExpression.class.php delete mode 100644 classes/db/queryparts/expression/DeleteExpression.class.php delete mode 100644 classes/db/queryparts/expression/Expression.class.php delete mode 100644 classes/db/queryparts/expression/InsertExpression.class.php delete mode 100644 classes/db/queryparts/expression/SelectExpression.class.php delete mode 100644 classes/db/queryparts/expression/StarExpression.class.php delete mode 100644 classes/db/queryparts/expression/UpdateExpression.class.php delete mode 100644 classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php delete mode 100644 classes/db/queryparts/limit/Limit.class.php delete mode 100644 classes/db/queryparts/order/OrderByColumn.class.php delete mode 100644 classes/db/queryparts/table/IndexHint.class.php delete mode 100644 classes/db/queryparts/table/JoinTable.class.php delete mode 100644 classes/db/queryparts/table/MysqlTableWithHint.class.php delete mode 100644 classes/db/queryparts/table/Table.class.php delete mode 100644 classes/xml/XmlQueryParser.class.php delete mode 100644 classes/xml/xmlquery/DBParser.class.php delete mode 100644 classes/xml/xmlquery/QueryParser.class.php delete mode 100644 classes/xml/xmlquery/argument/Argument.class.php delete mode 100644 classes/xml/xmlquery/argument/ConditionArgument.class.php delete mode 100644 classes/xml/xmlquery/argument/SortArgument.class.php delete mode 100644 classes/xml/xmlquery/queryargument/DefaultValue.class.php delete mode 100644 classes/xml/xmlquery/queryargument/QueryArgument.class.php delete mode 100644 classes/xml/xmlquery/queryargument/SortQueryArgument.class.php delete mode 100644 classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php delete mode 100644 classes/xml/xmlquery/tags/column/ColumnTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/InsertColumnTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php delete mode 100644 classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/SelectColumnTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php delete mode 100644 classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php delete mode 100644 classes/xml/xmlquery/tags/condition/ConditionTag.class.php delete mode 100644 classes/xml/xmlquery/tags/condition/ConditionsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/group/GroupsTag.class.php delete mode 100644 classes/xml/xmlquery/tags/navigation/IndexTag.class.php delete mode 100644 classes/xml/xmlquery/tags/navigation/LimitTag.class.php delete mode 100644 classes/xml/xmlquery/tags/navigation/NavigationTag.class.php delete mode 100644 classes/xml/xmlquery/tags/query/QueryTag.class.php delete mode 100644 classes/xml/xmlquery/tags/table/HintTableTag.class.php delete mode 100644 classes/xml/xmlquery/tags/table/TableTag.class.php delete mode 100644 classes/xml/xmlquery/tags/table/TablesTag.class.php diff --git a/classes/db/DB.class.php b/classes/db/DB.class.php index e325b185d..393d34779 100644 --- a/classes/db/DB.class.php +++ b/classes/db/DB.class.php @@ -1,1228 +1,6 @@ */ -/** - * - 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 +class DB extends Rhymix\Framework\DB { - - static $isSupported = FALSE; - - /** - * priority of DBMS - * @var array - */ - protected static $priority_dbms = array( - 'mysql' => 1, - ); - - /** - * master database connection string - * @var array - */ - protected $master_db = NULL; - - /** - * array of slave databases connection strings - * @var array - */ - protected $slave_db = NULL; - protected $result = NULL; - - /** - * error code (0 means no error) - * @var int - */ - protected $errno = 0; - - /** - * error message - * @var string - */ - protected $errstr = ''; - - /** - * query string of latest executed query - * @var string - */ - protected $query = ''; - protected $connection = ''; - - /** - * elapsed time of latest executed query - * @var int - */ - protected $elapsed_time = 0; - - /** - * elapsed time of latest executed DB class - * @var int - */ - protected $elapsed_dbclass_time = 0; - - /** - * transaction flag - * @var boolean - */ - protected $transaction_started = FALSE; - protected $is_connected = FALSE; - - /** - * returns enable list in supported dbms list - * will be written by classes/DB/DB***.class.php - * @var array - */ - protected static $supported_list = array(); - - /** - * location of query cache - * @var string - */ - protected $cache_file = 'files/cache/queries/'; - - /** - * stores database type, e.g. mysql - * @var string - */ - public $db_type; - public $db_version = ''; - - /** - * flag to decide if class prepared statements or not (when supported); can be changed from db.config.info - * @var string - */ - public $use_prepared_statements; - - /** - * leve of transaction - * @var unknown - */ - protected $transactionNestedLevel = 0; - - /** - * returns instance of certain db type - * @param string $db_type type of db - * @return DB return DB object instance - */ - public static function getInstance($db_type = NULL) - { - if(!$db_type) - { - $db_type = config('db.master.type'); - } - if(!$db_type && Context::isInstalled()) - { - Rhymix\Framework\Debug::displayError(lang('msg_db_not_setted')); - exit; - } - if(!strncmp($db_type, 'mysql', 5)) - { - $db_type = 'mysql'; - } - - if(!isset($GLOBALS['__DB__'])) - { - $GLOBALS['__DB__'] = array(); - } - if(!isset($GLOBALS['__DB__'][$db_type])) - { - $class_name = 'DB' . ucfirst($db_type); - $class_file = RX_BASEDIR . "classes/db/$class_name.class.php"; - if(!file_exists($class_file)) - { - Rhymix\Framework\Debug::displayError(sprintf('DB type "%s" is not supported.', $db_type)); - exit; - } - - // get a singletone instance of the database driver class - require_once($class_file); - $GLOBALS['__DB__'][$db_type] = new $class_name; - $GLOBALS['__DB__'][$db_type]->db_type = $db_type; - } - - return $GLOBALS['__DB__'][$db_type]; - } - - /** - * returns instance of db - * @return DB return DB object instance - */ - public static function create() - { - return new static(); - } - - /** - * constructor - * @return void - */ - public function __construct() - { - $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 - */ - public static function getSupportedList() - { - return self::_getSupportedList(); - } - - /** - * returns enable list in supported dbms list - * this list return by child class - * @return array return enable DBMS list in supported dbms list - */ - public static function getEnableList() - { - if(!self::$supported_list) - { - $oDB = new DB(); - self::$supported_list = self::_getSupportedList(); - } - - $enableList = array(); - if(is_array(self::$supported_list)) - { - foreach(self::$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 - */ - public static function getDisableList() - { - if(!self::$supported_list) - { - $oDB = new DB(); - self::$supported_list = self::_getSupportedList(); - } - - $disableList = array(); - if(is_array(self::$supported_list)) - { - foreach(self::$supported_list AS $key => $value) - { - if(!$value->enable) - { - $disableList[] = $value; - } - } - } - return $disableList; - } - - /** - * returns list of supported dbms list - * - * @return array return supported DBMS list - */ - protected static function _getSupportedList() - { - if(self::$supported_list) - { - return self::$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 - foreach ($supported_list as $db_type) - { - if (strtolower($db_type) !== 'mysql') - { - continue; - } - $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; - } - - require_once($class_file); - $oDB = new $class_name(); - - $obj = new stdClass; - $obj->db_type = $db_type; - $obj->enable = $oDB->isSupported() ? TRUE : FALSE; - unset($oDB); - - $get_supported_list[] = $obj; - } - - // sort - usort($get_supported_list, function($a, $b) { - $priority_a = isset(self::$priority_dbms[$a->db_type]) ? self::$priority_dbms[$a->db_type] : 0; - $priority_b = isset(self::$priority_dbms[$b->db_type]) ? self::$priority_dbms[$b->db_type] : 0; - return $a - $b; - }); - - return self::$supported_list = $get_supported_list; - } - - /** - * Return dbms supportable status - * The value is set in the child class - * @return boolean true: is supported, false: is not supported - */ - public 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 - */ - public 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 - */ - public function actStart($query) - { - $this->setError(0, 'success'); - $this->query = $query; - $this->act_start = microtime(true); - $this->elapsed_time = 0; - } - - /** - * finish recording log - * @return void - */ - public function actFinish() - { - if(!$this->query) - { - return; - } - $this->act_finish = microtime(true); - $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'); - $log['backtrace'] = array(); - - if (config('debug.enabled') && ($this->isError() || in_array('queries', config('debug.display_content') ?: array()))) - { - $bt = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); - foreach($bt as $no => $call) - { - if($call['function'] == 'executeQuery' || $call['function'] == 'executeQueryArray') - { - $call_no = $no; - $call_no++; - $log['called_file'] = $bt[$call_no]['file']; - $log['called_line'] = $bt[$call_no]['line']; - $call_no++; - $log['called_method'] = $bt[$call_no]['class'].$bt[$call_no]['type'].$bt[$call_no]['function']; - $log['backtrace'] = array_slice($bt, $call_no, 1); - break; - } - } - } - else - { - $log['called_file'] = $log['called_line'] = $log['called_method'] = null; - $log['backtrace'] = array(); - } - - // leave error log if an error occured - if($this->isError()) - { - $log['result'] = 'error'; - $log['errno'] = $this->errno; - $log['errstr'] = $this->errstr; - } - else - { - $log['result'] = 'success'; - $log['errno'] = null; - $log['errstr'] = null; - } - - $this->setQueryLog($log); - } - - /** - * set query debug log - * @param array $log values set query debug - * @return void - */ - public function setQueryLog($log) - { - Rhymix\Framework\Debug::addQuery($log); - } - - /** - * set error - * @param int $errno error code - * @param string $errstr error message - * @return void - */ - public function setError($errno = 0, $errstr = 'success') - { - $this->errno = $errno; - $this->errstr = $errstr; - } - - /** - * Return error status - * @return boolean true: error, false: no error - */ - public function isError() - { - return ($this->errno !== 0); - } - - /** - * Returns object of error info - * @return object object of error - */ - public function getError() - { - $this->errstr = Context::convertEncodingStr($this->errstr); - return new BaseObject($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 - */ - public function executeQuery($query_id, $args = NULL, $arg_columns = NULL, $type = NULL) - { - static $cache_file = array(); - - if(!$query_id) - { - return new BaseObject(-1, 'msg_invalid_queryid'); - } - if(!$this->db_type) - { - return new BaseObject(-1, 'msg_db_not_setted'); - } - - $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 new BaseObject(-1, 'msg_invalid_queryid'); - } - $module = $id_args[1]; - $id = $id_args[2]; - } - if(!$target || !$module || !$id) - { - $this->actDBClassFinish(); - return new BaseObject(-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 BaseObject(-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 - */ - public 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')) - { - $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 - */ - public 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 BaseObject(-1, 'msg_invalid_queryid'); - } - - if (is_object($source_args)) - { - $args = clone $source_args; - } - elseif (is_array($source_args)) - { - $args = (object)$source_args; - } - else - { - $args = null; - } - - $output = include($cache_file); - - if($output instanceof BaseObject && !$output->toBool()) - { - return $output; - } - if(!is_object($output) || !method_exists($output, 'getAction')) - { - return new BaseObject(-1, sprintf(lang('msg_failed_to_load_query'), $query_id)); - } - - // 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(); - } - elseif(!($output instanceof BaseObject)) - { - $output = new BaseObject(); - } - $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 - */ - public function getCountCache($tables, $condition) - { - return FALSE; - } - - /** - * 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 - */ - public function putCountCache($tables, $condition, $count = 0) - { - return FALSE; - } - - /** - * Reset counter cache data - * @param array|string $tables tables to reset cache data - * @return boolean true: success, false: failed - */ - public function resetCountCache($tables) - { - return FALSE; - } - - /** - * Drop tables - * @param string $table_name - * @return void - */ - public 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 - */ - public function getSelectSql($query, $with_values = TRUE) - { - $select = $query->getSelectString($with_values); - if($select == '') - { - return new BaseObject(-1, "Invalid query"); - } - $select = 'SELECT ' . $select; - - $from = $query->getFromString($with_values); - if($from == '') - { - return new BaseObject(-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; - } - - $having = $query->getHavingString($with_values); - if($having != '') - { - $having = ' HAVING ' . $having; - } - - $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 $having $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 - */ - public 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 - */ - public 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 BaseObject(-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 - */ - public function getUpdateSql($query, $with_values = TRUE, $with_priority = FALSE) - { - $columnsList = $query->getUpdateString($with_values); - if($columnsList == '') - { - return new BaseObject(-1, "Invalid query"); - } - - $tables = $query->getFromString($with_values); - if($tables == '') - { - return new BaseObject(-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 - */ - public 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 - */ - public 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 - */ - public function _getConnection($type = 'master', $indx = NULL) - { - if($type == 'master' || $this->transactionNestedLevel) - { - if(!$this->master_db['is_connected']) - { - $this->_connect($type); - } - $this->connection = 'master (' . $this->master_db['host'] . ')'; - return $this->master_db["resource"]; - } - - if($indx === NULL) - { - $indx = $this->_getSlaveConnectionStringIndex($type); - } - - if($this->slave_db[$indx]['host'] == $this->master_db['host'] && $this->slave_db[$indx]['port'] == $this->master_db['port']) - { - if(!$this->master_db['is_connected']) - { - $this->_connect($type); - } - $this->connection = 'master (' . $this->master_db['host'] . ')'; - return $this->master_db["resource"]; - } - - if(!$this->slave_db[$indx]['is_connected']) - { - $this->_connect($type, $indx); - } - $this->connection = 'slave (' . $this->slave_db[$indx]['host'] . ')'; - return $this->slave_db[$indx]["resource"]; - } - - /** - * check db information exists - * @return boolean - */ - public function _dbInfoExists() - { - return $this->master_db ? true : false; - } - - /** - * DB disconnection - * - * @param resource $connection - * @return void - */ - protected function _close($connection) - { - - } - - /** - * DB disconnection - * @param string $type 'master' or 'slave' - * @param int $indx number in slave dbms server list - * @return void - */ - public 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 - */ - protected function _begin($transactionLevel = 0) - { - return TRUE; - } - - /** - * DB transaction start - * @return void - */ - public 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 - */ - protected function _rollback($transactionLevel = 0) - { - return TRUE; - } - - /** - * DB transaction rollback - * @return void - */ - public 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 - */ - protected function _commit() - { - return TRUE; - } - - /** - * DB transaction commit - * @param boolean $force regardless transaction start status or connect status, forced to commit - * @return void - */ - public 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 - */ - protected function __query($query, $connection) - { - - } - - /** - * Execute the query - * - * @param string $query - * @param resource $connection - * @return resource - */ - public 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 - */ - protected function _setDBInfo() - { - $db_info = config('db'); - $this->master_db = $db_info['master']; - $this->slave_db = $db_info ? array_values($db_info) : null; - $this->prefix = $this->master_db['prefix']; - $this->use_prepared_statements = config('use_prepared_statements'); - } - - /** - * DB Connect - * this method is protected - * @param array $connection - * @return void - */ - protected function __connect($connection) - { - - } - - /** - * If have a task after connection, add a taks in this method - * this method is protected - * @param resource $connection - * @return void - */ - protected 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 - */ - protected 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 = $type . ' (' . $connection['host'] . ')'; - - // regist $this->close callback - register_shutdown_function(array($this, "close")); - - $this->_afterConnect($result); - } - - /** - * Start recording DBClass log - * @return void - */ - public function actDBClassStart() - { - $this->setError(0, 'success'); - $this->act_dbclass_start = microtime(true); - $this->elapsed_dbclass_time = 0; - } - - /** - * Finish recording DBClass log - * @return void - */ - public function actDBClassFinish() - { - if(!$this->query) - { - return; - } - $this->act_dbclass_finish = microtime(true); - $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 - */ - public function getParser($force = FALSE) - { - static $dbParser = NULL; - if(!$dbParser || $force) - { - $oDB = DB::getInstance(); - $dbParser = $oDB->getParser(); - } - - return $dbParser; - } - - /** - * Get the number of rows affected by the last query - * @return int - */ - public function getAffectedRows() - { - return -1; - } - - /** - * Get the ID generated in the last query - * @return int - */ - public function getInsertID() - { - return 0; - } + } -/* End of file DB.class.php */ -/* Location: ./classes/db/DB.class.php */ diff --git a/classes/db/DBMysql.class.php b/classes/db/DBMysql.class.php deleted file mode 100644 index 8729d1135..000000000 --- a/classes/db/DBMysql.class.php +++ /dev/null @@ -1,1171 +0,0 @@ - */ - -/** - * Merged class for MySQL and MySQLi, with or without InnoDB - */ -class DBMySQL extends DB -{ - /** - * prefix of a tablename (One or more Rhymix can be installed in a single DB) - * @var string - */ - var $prefix = 'rx_'; - var $comment_syntax = '/* %s */'; - var $charset = 'utf8mb4'; - - /** - * Column type used in MySQL - * - * Becasue a common column type in schema/query xml is used for colum_type, - * it should be replaced properly for each DBMS - * @var array - */ - var $column_type = array( - 'bignumber' => 'bigint', - 'number' => 'bigint', - 'varchar' => 'varchar', - 'char' => 'char', - 'text' => 'text', - 'bigtext' => 'longtext', - 'date' => 'varchar(14)', - 'float' => 'float', - ); - - /** - * Last statement executed - */ - var $last_stmt; - - /** - * Query parameters for prepared statement - */ - var $params = array(); - - /** - * Constructor - * @return void - */ - function __construct() - { - $this->_setDBInfo(); - $this->_connect(); - } - - /** - * DB Connect - * this method is private - * @param array $connection connection's value is db_hostname, db_port, db_database, db_userid, db_password - * @return mysqli - */ - protected function __connect($connection) - { - // Attempt to connect - if($connection['port']) - { - $mysqli = new mysqli($connection['host'], $connection['user'], $connection['pass'], $connection['database'], $connection['port']); - } - else - { - $mysqli = new mysqli($connection['host'], $connection['user'], $connection['pass'], $connection['database']); - } - - // Check connection error - if($mysqli->connect_errno) - { - Rhymix\Framework\Debug::displayError(sprintf('DB ERROR %d : %s', $mysqli->connect_errno, $mysqli->connect_error)); - exit; - } - - // Set DB charset - $this->charset = isset($connection['charset']) ? $connection['charset'] : 'utf8'; - $mysqli->set_charset($this->charset); - - return $mysqli; - } - - /** - * DB disconnection - * this method is private - * @param mysqli $connection - * @return void - */ - protected function _close($connection) - { - if ($connection instanceof mysqli) - { - $connection->close(); - } - } - - /** - * Handles quatation of the string variables from the query - * @param string $string - * @return string - */ - function addQuotes($string) - { - if(!is_numeric($string)) - { - $connection = $this->_getConnection('master'); - $string = $connection->real_escape_string($string); - } - return $string; - } - - /** - * DB transaction start - * this method is private - * @return boolean - */ - protected function _begin($transactionLevel = 0) - { - $connection = $this->_getConnection('master'); - - if(!$transactionLevel) - { - $connection->begin_transaction(); - $this->setQueryLog(array('query' => 'START TRANSACTION')); - } - else - { - $this->_query("SAVEPOINT SP" . $transactionLevel, $connection); - } - return true; - } - - /** - * DB transaction rollback - * this method is private - * @return boolean - */ - protected function _rollback($transactionLevel = 0) - { - $connection = $this->_getConnection('master'); - $point = $transactionLevel - 1; - - if($point) - { - $this->_query("ROLLBACK TO SP" . $point, $connection); - } - else - { - $connection->rollback(); - $this->setQueryLog(array('query' => 'ROLLBACK')); - } - return true; - } - - /** - * DB transaction commit - * this method is private - * @return boolean - */ - protected function _commit() - { - $connection = $this->_getConnection('master'); - $connection->commit(); - $this->setQueryLog(array('query' => 'COMMIT')); - return true; - } - - /** - * Execute the query - * this method is private - * @param string $query - * @param resource $connection - * @return resource - */ - function __query($query, $connection) - { - if (!($connection instanceof mysqli) || $connection->connection_errno) - { - $this->setError(-1, 'Unable to connect to DB.'); - return false; - } - - if($this->use_prepared_statements == 'Y') - { - // 1. Prepare query - $stmt = $connection->prepare($query); - if(!$stmt) - { - $this->setError($connection->errno, $connection->error); - return $this->last_stmt = $result; - } - - // 2. Bind parameters - if ($this->params) - { - $types = ''; - $params = array(); - foreach($this->params as $k => $o) - { - $value = $o->getUnescapedValue(); - $type = $o->getType(); - - // Skip column names -> this should be concatenated to query string - if($o->isColumnName()) - { - continue; - } - - switch($type) - { - case 'number' : - $type = 'i'; - break; - case 'varchar' : - $type = 's'; - break; - default: - $type = 's'; - } - - if(is_array($value)) - { - foreach($value as $v) - { - $params[] = $v; - $types .= $type; - } - } - else - { - $params[] = $value; - $types .= $type; - } - } - - // 2. Bind parameters - $args = array(); - $args[0] = $stmt; - $args[1] = $types; - $i = 2; - foreach($params as $key => $param) - { - $copy[$key] = $param; - $args[$i++] = &$copy[$key]; - } - - $status = call_user_func_array('mysqli_stmt_bind_param', $args); - if(!$status) - { - $this->setError(-1, "Invalid arguments: " . $connection->error); - return $this->last_stmt = $stmt; - } - } - - // 3. Execute query - $status = $stmt->execute(); - if(!$status) - { - $this->setError(-1, "Prepared statement failed: " . $connection->error); - return $this->last_stmt = $stmt; - } - - // Return stmt for other processing - return $this->last_stmt = $stmt; - } - else - { - $result = $connection->query($query); - if($connection->errno) - { - $this->setError($connection->errno, $connection->error); - } - - return $this->last_stmt = $result; - } - } - - /** - * Fetch the result - * @param resource $result - * @param int|NULL $arrayIndexEndValue - * @return array - */ - function _fetch($result, $arrayIndexEndValue = NULL) - { - $output = array(); - if(!$this->isConnected() || $this->isError() || !$result) - { - return $output; - } - - // No prepared statements - if($this->use_prepared_statements != 'Y') - { - while($tmp = $this->db_fetch_object($result)) - { - if($arrayIndexEndValue) - { - $output[$arrayIndexEndValue--] = $tmp; - } - else - { - $output[] = $tmp; - } - } - $result->free_result(); - } - - // Prepared stements: bind result variable and fetch data - else - { - $stmt = $result; - $fields = $stmt->result_metadata()->fetch_fields(); - $row = array(); - $resultArray = array(); - - /** - * Mysqli has a bug that causes LONGTEXT columns not to get loaded - * Unless store_result is called before - * MYSQLI_TYPE for longtext is 252 - */ - $longtext_exists = false; - foreach($fields as $field) - { - // When joined tables are used and the same column name appears twice, we should add it separately, otherwise bind_result fails - if(isset($resultArray[$field->name])) - { - $field->name = 'repeat_' . $field->name; - } - - // Array passed needs to contain references, not values - $row[$field->name] = ''; - $resultArray[$field->name] = &$row[$field->name]; - - if($field->type == 252) - { - $longtext_exists = true; - } - } - $resultArray = array_merge(array($stmt), $resultArray); - - if($longtext_exists) - { - $stmt->store_result(); - } - - call_user_func_array('mysqli_stmt_bind_result', $resultArray); - array_shift($resultArray); - - while($stmt->fetch()) - { - $resultObject = new stdClass; - foreach($resultArray as $key => $value) - { - if(strpos($key, 'repeat_')) - { - $key = substr($key, 6); - } - $resultObject->$key = $value; - } - - if($arrayIndexEndValue) - { - $output[$arrayIndexEndValue--] = $resultObject; - } - else - { - $output[] = $resultObject; - } - } - - $stmt->free_result(); - $stmt->close(); - } - - // Return object if there is only 1 result. - if(count($output) == 1) - { - if(isset($arrayIndexEndValue)) - { - return $output; - } - else - { - return $output[0]; - } - } - else - { - return $output; - } - } - - /** - * Return the sequence value incremented by 1 - * Auto_increment column only used in the sequence table - * @return int - */ - function getNextSequence() - { - $query = sprintf("INSERT INTO `%ssequence` (seq) VALUES ('0')", $this->prefix); - $this->_query($query); - $sequence = $this->getInsertID(); - if($sequence % 10000 == 0) - { - $query = sprintf("DELETE FROM `%ssequence` WHERE seq < %d", $this->prefix, $sequence); - $this->_query($query); - } - - return $sequence; - } - - /** - * Function to obtain mysql old password(mysql only) - * @param string $password input password - * @param string $saved_password saved password in DBMS - * @return boolean - */ - function isValidOldPassword($password, $saved_password) - { - $query = sprintf("SELECT PASSWORD('%s') AS password, OLD_PASSWORD('%s') AS old_password", $this->addQuotes($password), $this->addQuotes($password)); - $result = $this->_query($query); - $tmp = $this->_fetch($result); - if($tmp->password === $saved_password || $tmp->old_password === $saved_password) - { - return true; - } - return false; - } - - /** - * Check a table exists status - * @param string $target_name - * @return boolean - */ - function isTableExists($target_name) - { - $query = sprintf("SHOW TABLES LIKE '%s%s'", $this->prefix, $this->addQuotes($target_name)); - $result = $this->_query($query); - $tmp = $this->_fetch($result); - if(!$tmp) - { - return false; - } - return true; - } - - /** - * Add a column to the table - * @param string $table_name table name - * @param string $column_name column name - * @param string $type column type, default value is 'number' - * @param int $size column size - * @param string|int $default default value - * @param boolean $notnull not null status, default value is false - * @return void - */ - function addColumn($table_name, $column_name, $type = 'number', $size = '', $default = null, $notnull = false, $after_column = null) - { - $type = strtolower($type); - $type = isset($this->column_type[$type]) ? $this->column_type[$type] : $type; - if(in_array($type, ['integer', 'int', 'bigint', 'smallint'])) - { - $size = ''; - } - - $query = sprintf("ALTER TABLE `%s%s` ADD `%s` ", $this->prefix, $table_name, $column_name); - if($size) - { - $query .= sprintf(" %s(%s) ", $type, $size); - } - else - { - $query .= sprintf(" %s ", $type); - } - if(isset($default)) - { - $query .= sprintf(" DEFAULT '%s' ", $default); - } - if($notnull) - { - $query .= " NOT NULL "; - } - if($after_column) - { - $query .= sprintf(" AFTER `%s` ", $after_column); - } - - return $this->_query($query); - } - - /** - * Drop a column from the table - * @param string $table_name table name - * @param string $column_name column name - * @return void - */ - function dropColumn($table_name, $column_name) - { - $query = sprintf("ALTER TABLE `%s%s` DROP `%s` ", $this->prefix, $table_name, $column_name); - $this->_query($query); - } - - /** - * Modify a column - * @param string $table_name table name - * @param string $column_name column name - * @param string $type column type, default value is 'number' - * @param int $size column size - * @param string|int $default default value - * @param boolean $notnull not null status, default value is false - * @return bool - */ - function modifyColumn($table_name, $column_name, $type = 'number', $size = '', $default = '', $notnull = false) - { - $type = strtolower($type); - $type = isset($this->column_type[$type]) ? $this->column_type[$type] : $type; - if(in_array($type, ['integer', 'int', 'bigint', 'smallint'])) - { - $size = ''; - } - - $query = sprintf("ALTER TABLE `%s%s` MODIFY `%s` ", $this->prefix, $table_name, $column_name); - if($size) - { - $query .= sprintf(" %s(%s) ", $type, $size); - } - else - { - $query .= sprintf(" %s ", $type); - } - if($default) - { - $query .= sprintf(" DEFAULT '%s' ", $default); - } - if($notnull) - { - $query .= " NOT NULL "; - } - - return $this->_query($query) ? true : false; - } - - /** - * Check column exist status of the table - * @param string $table_name table name - * @param string $column_name column name - * @return boolean - */ - function isColumnExists($table_name, $column_name) - { - $query = sprintf("SHOW FIELDS FROM `%s%s`", $this->prefix, $table_name); - $result = $this->_query($query); - if($this->isError()) - { - return; - } - $output = $this->_fetch($result); - if($output) - { - $column_name = strtolower($column_name); - foreach($output as $key => $val) - { - $name = strtolower($val->Field); - if($column_name == $name) - { - return true; - } - } - } - return false; - } - - /** - * Get information about a column - * @param string $table_name table name - * @param string $column_name column name - * @return BaseObject - */ - function getColumnInfo($table_name, $column_name) - { - $query = sprintf("SHOW FIELDS FROM `%s%s` WHERE `Field` = '%s'", $this->prefix, $table_name, $column_name); - $result = $this->_query($query); - if($this->isError()) - { - return; - } - $output = $this->_fetch($result); - if($output) - { - $dbtype = $output->{'Type'}; - if($xetype = array_search($dbtype, $this->column_type)) - { - $size = null; - } - elseif(strpos($dbtype, '(') !== false) - { - list($dbtype, $size) = explode('(', $dbtype, 2); - $size = intval(rtrim($size, ')')); - if($xetype = array_search($dbtype, $this->column_type)) - { - // no-op - } - else - { - $xetype = $dbtype; - } - } - else - { - $xetype = $dbtype; - $size = null; - } - return (object)array( - 'name' => $output->{'Field'}, - 'dbtype' => $dbtype, - 'xetype' => $xetype, - 'size' => $size, - 'default_value' => $output->{'Default'}, - 'notnull' => strncmp($output->{'Null'}, 'NO', 2) == 0 ? true : false, - ); - } - else - { - return false; - } - } - - /** - * Add an index to the table - * $target_columns = array(col1, col2) - * $is_unique? unique : none - * @param string $table_name table name - * @param string $index_name index name - * @param string|array $target_columns target column or columns - * @param boolean $is_unique - * @return void - */ - function addIndex($table_name, $index_name, $target_columns, $is_unique = false) - { - if(!is_array($target_columns)) - { - $target_columns = array($target_columns); - } - - $query = sprintf("ALTER TABLE `%s%s` ADD %s INDEX `%s` (%s);", $this->prefix, $table_name, $is_unique ? 'UNIQUE' : '', $index_name, implode(',', $target_columns)); - $this->_query($query); - } - - /** - * Drop an index from the table - * @param string $table_name table name - * @param string $index_name index name - * @param boolean $is_unique - * @return void - */ - function dropIndex($table_name, $index_name, $is_unique = false) - { - $query = sprintf("ALTER TABLE `%s%s` DROP INDEX `%s`", $this->prefix, $table_name, $index_name); - $this->_query($query); - } - - /** - * Check index status of the table - * @param string $table_name table name - * @param string $index_name index name - * @return boolean - */ - function isIndexExists($table_name, $index_name) - { - $query = sprintf("SHOW INDEXES FROM `%s%s`", $this->prefix, $table_name); - $result = $this->_query($query); - if($this->isError()) - { - return; - } - $output = $this->_fetch($result); - if(!$output) - { - return; - } - if(!is_array($output)) - { - $output = array($output); - } - - for($i = 0; $i < count($output); $i++) - { - if($output[$i]->Key_name == $index_name) - { - return true; - } - } - return false; - } - - /** - * Creates a table by using xml contents - * @param string $xml_doc xml schema contents - * @return void|object - */ - function createTableByXml($xml_doc) - { - return $this->_createTable($xml_doc); - } - - /** - * Creates a table by using xml file path - * @param string $file_name xml schema file path - * @return void|object - */ - function createTableByXmlFile($file_name) - { - if(!file_exists($file_name)) - { - return; - } - // read xml file - $buff = FileHandler::readFile($file_name); - return $this->_createTable($buff); - } - - /** - * Create table by using the schema xml - * - * type : number, varchar, tinytext, text, bigtext, char, date, \n - * opt : notnull, default, size\n - * index : primary key, index, unique\n - * @param string $xml_doc xml schema contents - * @return void|object - */ - function _createTable($xml_doc) - { - // Get table definition. - $table = Rhymix\Framework\Parsers\DBTableParser::loadXML('', $xml_doc); - if (!$table) - { - return false; - } - - // Execute the CREATE TABLE query. - $schema = $table->getCreateQuery($this->prefix, $this->charset, config('db.master.engine')); - $output = $this->_query($schema); - return $output ? true : false; - } - - /** - * Drop table - * - * @param string $name - * @return bool - */ - function dropTable($table_name) - { - // Generate the drop query - $query = sprintf('DROP TABLE `%s`', $this->addQuotes($this->prefix . $table_name)); - - // Execute the drop query - $output = $this->_query($query); - if($output) - { - return true; - } - else - { - return false; - } - } - - /** - * Handles insertAct - * @param BaseObject $queryObject - * @param boolean $with_values - * @return mixed - */ - function _executeInsertAct($queryObject, $with_values = true) - { - if ($this->use_prepared_statements == 'Y') - { - $this->params = $queryObject->getArguments(); - $with_values = false; - } - - $query = $this->getInsertSql($queryObject, $with_values, true); - if ($query instanceof BaseObject) - { - $this->params = array(); - return $query; - } - else - { - $output = $this->_query($query); - $this->params = array(); - return $output; - } - } - - /** - * Handles updateAct - * @param BaseObject $queryObject - * @param boolean $with_values - * @return mixed - */ - function _executeUpdateAct($queryObject, $with_values = true) - { - if ($this->use_prepared_statements == 'Y') - { - $this->params = $queryObject->getArguments(); - $with_values = false; - } - - $query = $this->getUpdateSql($queryObject, $with_values, true); - if ($query instanceof BaseObject) - { - $this->params = array(); - return $query; - } - else - { - $output = $this->_query($query); - $this->params = array(); - return $output; - } - } - - /** - * Handles deleteAct - * @param BaseObject $queryObject - * @param boolean $with_values - * @return mixed - */ - function _executeDeleteAct($queryObject, $with_values = true) - { - if ($this->use_prepared_statements == 'Y') - { - $this->params = $queryObject->getArguments(); - $with_values = false; - } - - $query = $this->getDeleteSql($queryObject, $with_values, true); - if ($query instanceof BaseObject) - { - $this->params = array(); - return $query; - } - else - { - $output = $this->_query($query); - $this->params = array(); - return $output; - } - } - - /** - * Handle selectAct - * In order to get a list of pages easily when selecting \n - * it supports a method as navigation - * @param BaseObject $queryObject - * @param resource $connection - * @param boolean $with_values - * @return BaseObject - */ - function _executeSelectAct($queryObject, $connection = null, $with_values = true) - { - if ($this->use_prepared_statements == 'Y') - { - $this->params = $queryObject->getArguments(); - $with_values = false; - } - - $result = null; - $limit = $queryObject->getLimit(); - if($limit && $limit->isPageHandler()) - { - $output = $this->queryPageLimit($queryObject, $result, $connection, $with_values); - $this->params = array(); - return $output; - } - else - { - $query = $this->getSelectSql($queryObject, $with_values); - if($query instanceof BaseObject) - { - $this->params = array(); - return $query; - } - - $result = $this->_query($query, $connection); - if($this->isError()) - { - $this->params = array(); - return $this->queryError($queryObject); - } - - $data = $this->_fetch($result); - $buff = new BaseObject; - $buff->data = $data; - - if($queryObject->usesClickCount()) - { - $update_query = $this->getClickCountQuery($queryObject); - $this->_executeUpdateAct($update_query, $with_values); - } - - $this->params = array(); - return $buff; - } - } - - /** - * Get the number of rows affected by the last query - * @return int - */ - function getAffectedRows() - { - $stmt = $this->last_stmt; - return $stmt ? $stmt->affected_rows : -1; - } - - /** - * Get the ID generated in the last query - * @return int - */ - function getInsertID() - { - $connection = $this->_getConnection('master'); - return $connection->insert_id; - } - - /** - * @deprecated - * @return int - */ - function db_insert_id() - { - return $this->getInsertID(); - } - - /** - * Fetch a result row as an object - * @param resource $result - * @return BaseObject - */ - function db_fetch_object($result) - { - return $result->fetch_object(); - } - - /** - * Free result memory - * @param resource $result - * @return boolean Returns TRUE on success or FALSE on failure. - */ - function db_free_result($result) - { - return $result->free_result(); - } - - /** - * Return the DBParser - * @param boolean $force - * @return DBParser - */ - function getParser($force = FALSE) - { - $dbParser = new DBParser('`', '`', $this->prefix); - return $dbParser; - } - - /** - * If have a error, return error object - * @param BaseObject $queryObject - * @return BaseObject - */ - function queryError($queryObject) - { - $limit = method_exists($queryObject, 'getLimit') ? $queryObject->getLimit() : false; - if($limit && $limit->isPageHandler()) - { - $buff = new BaseObject; - $buff->total_count = 0; - $buff->total_page = 0; - $buff->page = 1; - $buff->data = array(); - $buff->page_navigation = new PageHandler(/* $total_count */0, /* $total_page */1, /* $page */1, /* $page_count */10); //default page handler values - return $buff; - } - else - { - return; - } - } - - /** - * If select query execute, return page info - * @param BaseObject $queryObject - * @param resource $result - * @param resource $connection - * @param boolean $with_values - * @return BaseObject Object with page info containing - */ - function queryPageLimit($queryObject, $result, $connection, $with_values = true) - { - $limit = $queryObject->getLimit(); - // Total count - $temp_where = $queryObject->getWhereString($with_values, false); - $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString($with_values), ($temp_where === '' ? '' : ' WHERE ' . $temp_where)); - - // Check for distinct query and if found update count query structure - $temp_select = $queryObject->getSelectString($with_values); - $uses_distinct = stripos($temp_select, "distinct") !== false; - $uses_groupby = $queryObject->getGroupByString() != ''; - if($uses_distinct || $uses_groupby) - { - $count_query = sprintf('select %s %s %s %s' - , $temp_select == '*' ? '1' : $temp_select - , 'FROM ' . $queryObject->getFromString($with_values) - , ($temp_where === '' ? '' : ' WHERE ' . $temp_where) - , ($uses_groupby ? ' GROUP BY ' . $queryObject->getGroupByString() : '') - ); - - // If query uses grouping or distinct, count from original select - $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); - } - - $result_count = $this->_query($count_query, $connection); - $count_output = $this->_fetch($result_count); - $total_count = (int) (isset($count_output->count) ? $count_output->count : NULL); - - $list_count = $limit->list_count->getValue(); - if(!$list_count) - { - $list_count = 20; - } - $page_count = $limit->page_count->getValue(); - if(!$page_count) - { - $page_count = 10; - } - $page = $limit->page->getValue(); - if(!$page || $page < 1) - { - $page = 1; - } - - // total pages - if($total_count) - { - $total_page = (int) (($total_count - 1) / $list_count) + 1; - } - else - { - $total_page = 1; - } - - // check the page variables - if($page > $total_page) - { - // If requested page is bigger than total number of pages, return empty list - $buff = new BaseObject; - $buff->total_count = $total_count; - $buff->total_page = $total_page; - $buff->page = $page; - $buff->data = array(); - $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); - return $buff; - } - $start_count = ($page - 1) * $list_count; - - $query = $this->getSelectPageSql($queryObject, $with_values, $start_count, $list_count); - - $result = $this->_query($query, $connection); - if($this->isError()) - { - return $this->queryError($queryObject); - } - - $virtual_no = $total_count - ($page - 1) * $list_count; - $data = $this->_fetch($result, $virtual_no); - - $buff = new BaseObject; - $buff->total_count = $total_count; - $buff->total_page = $total_page; - $buff->page = $page; - $buff->data = $data; - $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); - return $buff; - } - - /** - * If select query execute, return paging sql - * @param object $query - * @param boolean $with_values - * @param int $start_count - * @param int $list_count - * @return string select paging sql - */ - function getSelectPageSql($query, $with_values = true, $start_count = 0, $list_count = 0) - { - $select = $query->getSelectString($with_values); - if($select == '') - { - return new BaseObject(-1, "Invalid query"); - } - $select = 'SELECT ' . $select; - - $from = $query->getFromString($with_values); - if($from == '') - { - return new BaseObject(-1, "Invalid query"); - } - $from = ' FROM ' . $from; - - $where = $query->getWhereString($with_values); - if($where != '') - { - $where = ' WHERE ' . $where; - } - - $groupBy = $query->getGroupByString(); - if($groupBy != '') - { - $groupBy = ' GROUP BY ' . $groupBy; - } - - $orderBy = $query->getOrderByString(); - if($orderBy != '') - { - $orderBy = ' ORDER BY ' . $orderBy; - } - - $limit = $query->getLimitString(); - if($limit != '') - { - $limit = sprintf(' LIMIT %d, %d', $start_count, $list_count); - } - - return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; - } - - /** - * Find out the best supported character set - * - * @return string - */ - function getBestSupportedCharset() - { - if($output = $this->_fetch($this->_query("SHOW CHARACTER SET LIKE 'utf8%'"))) - { - $mb4_support = false; - foreach($output as $row) - { - if($row->Charset === 'utf8mb4') - { - $mb4_support = true; - } - } - return $mb4_support ? 'utf8mb4' : 'utf8'; - } - else - { - return 'utf8'; - } - } -} - -DBMysql::$isSupported = class_exists('mysqli'); diff --git a/classes/db/queryparts/Query.class.php b/classes/db/queryparts/Query.class.php deleted file mode 100644 index 968bb2b3e..000000000 --- a/classes/db/queryparts/Query.class.php +++ /dev/null @@ -1,749 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts - * @version 0.1 - */ -class Query extends BaseObject -{ - - /** - * Query id, defined in query xml file - * @var string - */ - var $queryID; - - /** - * DML type, ex) INSERT, DELETE, UPDATE, SELECT - * @var string - */ - var $action; - - /** - * priority level ex)LOW_PRIORITY, HIGHT_PRIORITY - * @var string - */ - var $priority; - - /** - * column list - * @var string|array - */ - var $columns; - - /** - * table list - * @var string|array - */ - var $tables; - - /** - * condition list - * @var string|array - */ - var $conditions; - - /** - * group list - * @var string|array - */ - var $groups; - - /** - * having list - * @var string|array - */ - var $having; - - /** - * order list - * @var array - */ - var $orderby; - - /** - * limit count - * @var int - */ - var $limit; - - /** - * argument list - * @var array - */ - var $arguments = NULL; - - /** - * column list - * @var array - */ - var $columnList = NULL; - - /** - * order by text - * @var string - */ - var $_orderByString; - - /** - * constructor - * @param string $queryID - * @param string $action - * @param string|array $columns - * @param string|array $tables - * @param string|array $conditions - * @param string|array $groups - * @param string|array $orderby - * @param int $limit - * @param string $priority - * @return void - */ - function __construct($queryID = NULL - , $action = NULL - , $columns = NULL - , $tables = NULL - , $conditions = NULL - , $groups = NULL - , $orderby = NULL - , $limit = NULL - , $priority = NULL) - { - $this->queryID = $queryID; - $this->action = $action; - $this->priority = $priority; - - if(!isset($tables)) - { - return; - } - - $this->columns = $this->setColumns($columns); - $this->tables = $this->setTables($tables); - $this->conditions = $this->setConditions($conditions); - $this->groups = $this->setGroups($groups); - $this->orderby = $this->setOrder($orderby); - $this->limit = $this->setLimit($limit); - } - - function show() - { - return TRUE; - } - - function setQueryId($queryID) - { - $this->queryID = $queryID; - } - - function setAction($action) - { - $this->action = $action; - } - - function setPriority($priority) - { - $this->priority = $priority; - } - - function setColumnList($columnList) - { - if (!is_array($this->columnList)) return; - $this->columnList = $columnList; - - if(count($this->columnList) > 0) - { - $selectColumns = array(); - $dbParser = DB::getParser(); - - foreach($this->columnList as $columnName) - { - $columnName = $dbParser->escapeColumnExpression($columnName); - $selectColumns[] = new SelectExpression($columnName); - } - unset($this->columns); - $this->columns = $selectColumns; - } - } - - function setColumns($columns) - { - if(!isset($columns) || count($columns) === 0) - { - $this->columns = array(new StarExpression()); - return; - } - - if(!is_array($columns)) - { - $columns = array($columns); - } - - $this->columns = $columns; - } - - function setTables($tables) - { - if(!isset($tables) || count($tables) === 0) - { - $this->setError(TRUE); - $this->setMessage("You must provide at least one table for the query."); - return; - } - - if(!is_array($tables)) - { - $tables = array($tables); - } - - $this->tables = $tables; - } - - function setSubquery($subquery) - { - $this->subquery = $subquery; - } - - function setConditions($conditions) - { - $this->conditions = array(); - if(!isset($conditions) || count($conditions) === 0) - { - return; - } - if(!is_array($conditions)) - { - $conditions = array($conditions); - } - - foreach($conditions as $conditionGroup) - { - if($conditionGroup->show()) - { - $this->conditions[] = $conditionGroup; - } - } - } - - function setGroups($groups) - { - if(!isset($groups) || count($groups) === 0) - { - return; - } - if(!is_array($groups)) - { - $groups = array($groups); - } - - $this->groups = $groups; - } - - function setHaving($conditions) - { - $this->having = array(); - if(!isset($conditions) || count($conditions) === 0) - { - return; - } - if(!is_array($conditions)) - { - $conditions = array($conditions); - } - - foreach($conditions as $conditionGroup) - { - if($conditionGroup->show()) - { - $this->having[] = $conditionGroup; - } - } - } - - function setOrder($order) - { - if(!isset($order) || count($order) === 0) - { - return; - } - if(!is_array($order)) - { - $order = array($order); - } - - $this->orderby = $order; - } - - function getOrder() - { - return $this->orderby; - } - - function setLimit($limit = NULL) - { - if(!isset($limit)) - { - return; - } - $this->limit = $limit; - } - - // START Fluent interface - /** - * seleect set - * @param string|array $columns - * @return Query return Query instance - */ - function select($columns = NULL) - { - $this->action = 'select'; - $this->setColumns($columns); - return $this; - } - - /** - * from set - * @param string|array $tables - * @return Query return Query instance - */ - function from($tables) - { - $this->setTables($tables); - return $this; - } - - /** - * where set - * @param string|array $conditions - * @return Query return Query instance - */ - function where($conditions) - { - $this->setConditions($conditions); - return $this; - } - - /** - * groupBy set - * @param string|array $groups - * @return Query return Query instance - */ - function groupBy($groups) - { - $this->setGroups($groups); - return $this; - } - - /** - * orderBy set - * @param string|array $order - * @return Query return Query instance - */ - function orderBy($order) - { - $this->setOrder($order); - return $this; - } - - /** - * limit set - * @param int $limit - * @return Query return Query instance - */ - function limit($limit) - { - $this->setLimit($limit); - return $this; - } - - // END Fluent interface - - function getAction() - { - return $this->action; - } - - function getPriority() - { - return $this->priority ? 'LOW_PRIORITY' : ''; - } - - /** - * Check if current query uses the click count attribute - * For CUBRID, this statement uses the click count feature. - * For the other databases, using this attribute causes a query - * to produce both a select and an update - */ - function usesClickCount() - { - return count($this->getClickCountColumns()) > 0; - } - - function getClickCountColumns() - { - $click_count_columns = array(); - foreach($this->columns as $column) - { - if($column->show() && is_a($column, 'ClickCountExpression')) - { - $click_count_columns[] = $column; - } - } - return $click_count_columns; - } - - /** - * Return select sql - * @param boolean $with_values - * @return string - */ - function getSelectString($with_values = TRUE) - { - foreach($this->columns as $column) - { - if($column->show()) - { - if($column->isSubquery()) - { - $select[] = $column->toString($with_values) . ' as ' . $column->getAlias(); - } - else - { - $select[] = $column->getExpression($with_values); - } - } - } - return trim(implode($select, ', ')); - } - - /** - * Return update sql - * @param boolean $with_values - * @return string - */ - function getUpdateString($with_values = TRUE) - { - foreach($this->columns as $column) - { - if($column->show()) - { - $update[] = $column->getExpression($with_values); - } - } - - if(!$update) return; - return trim(implode($update, ', ')); - } - - /** - * Return insert sql - * @param boolean $with_values - * @return string - */ - function getInsertString($with_values = TRUE) - { - $columnsList = ''; - // means we have insert-select - if($this->subquery) - { - foreach($this->columns as $column) - { - $columnsList .= $column->getColumnName() . ', '; - } - $columnsList = substr($columnsList, 0, -2); - $selectStatement = $this->subquery->toString($with_values); - $selectStatement = substr($selectStatement, 1, -1); - return "($columnsList) \n $selectStatement"; - } - - $valuesList = ''; - foreach($this->columns as $column) - { - if($column->show()) - { - $columnsList .= $column->getColumnName() . ', '; - $valuesList .= $column->getValue($with_values) . ', '; - } - } - $columnsList = substr($columnsList, 0, -2); - $valuesList = substr($valuesList, 0, -2); - - return "($columnsList) \n VALUES ($valuesList)"; - } - - function getTables() - { - return $this->tables; - } - - /** - * from table_a - * from table_a inner join table_b on x=y - * from (select * from table a) as x - * from (select * from table t) as x inner join table y on y.x - * @param boolean $with_values - * @return string - */ - function getFromString($with_values = TRUE) - { - $from = ''; - $simple_table_count = 0; - foreach($this->tables as $table) - { - if($table->isJoinTable() || !$simple_table_count) - { - $from .= $table->toString($with_values) . ' '; - } - else - { - $from .= ', ' . $table->toString($with_values) . ' '; - } - - if(is_a($table, 'Subquery')) - { - $from .= $table->getAlias() ? ' as ' . $table->getAlias() . ' ' : ' '; - } - - $simple_table_count++; - } - if(trim($from) == '') - { - return ''; - } - return $from; - } - - /** - * Return where sql - * @param boolean $with_values - * @param boolean $with_optimization - * @return string - */ - function getWhereString($with_values = TRUE, $with_optimization = TRUE) - { - $where = ''; - $condition_count = 0; - - foreach($this->conditions as $conditionGroup) - { - if($condition_count === 0) - { - $conditionGroup->setPipe(""); - } - $condition_string = $conditionGroup->toString($with_values); - $where .= $condition_string; - $condition_count++; - } - - if($with_optimization && - (strstr($this->getOrderByString(), 'list_order') || strstr($this->getOrderByString(), 'update_order'))) - { - - if($condition_count !== 0) - { - $where = '(' . $where . ') '; - } - - foreach($this->orderby as $order) - { - $colName = $order->getColumnName(); - if(strstr($colName, 'list_order') || strstr($colName, 'update_order')) - { - $opt_condition = new ConditionWithoutArgument($colName, 2100000000, 'less', 'and'); - if($condition_count === 0) - { - $opt_condition->setPipe(""); - } - $where .= $opt_condition->toString($with_values) . ' '; - $condition_count++; - } - } - } - - return trim($where); - } - - /** - * Return groupby sql - * @return string - */ - function getGroupByString() - { - $groupBy = ''; - if($this->groups) - { - if($this->groups[0] !== "") - { - $groupBy = implode(', ', $this->groups); - } - } - return $groupBy; - } - - /** - * Return having sql - * @param boolean $with_values - * @return string - */ - function getHavingString($with_values = TRUE) - { - if(!is_array($this->having)) - { - return ''; - } - - $having = ''; - $condition_count = 0; - - foreach($this->having as $conditionGroup) - { - if($condition_count === 0) - { - $conditionGroup->setPipe(""); - } - $condition_string = $conditionGroup->toString($with_values); - $having .= $condition_string; - $condition_count++; - } - - return trim($having); - } - - /** - * Return orderby sql - * @return string - */ - function getOrderByString() - { - if(!$this->_orderByString) - { - if(countobj($this->orderby) === 0) - { - return ''; - } - $orderBy = ''; - foreach($this->orderby as $order) - { - $orderBy .= $order->toString() . ', '; - } - $orderBy = substr($orderBy, 0, -2); - $this->_orderByString = $orderBy; - } - return $this->_orderByString; - } - - function getLimit() - { - return $this->limit; - } - - /** - * Return limit sql - * @return string - */ - function getLimitString() - { - $limit = ''; - if(countobj($this->limit) > 0) - { - $limit = ''; - $limit .= $this->limit->toString(); - } - return $limit; - } - - function getFirstTableName() - { - return $this->tables[0]->getName(); - } - - /** - * Return argument list - * @return array - */ - function getArguments() - { - if(!isset($this->arguments)) - { - $this->arguments = array(); - - // Join table arguments - if(countobj($this->tables) > 0) - { - foreach($this->tables as $table) - { - if($table->isJoinTable() || is_a($table, 'Subquery')) - { - $args = $table->getArguments(); - if($args) - { - $this->arguments = array_merge($this->arguments, $args); - } - } - } - } - - // Column arguments - // The if is for delete statements, all others must have columns - if(countobj($this->columns) > 0) - { - foreach($this->columns as $column) - { - if($column->show()) - { - $args = $column->getArguments(); - if($args) - { - $this->arguments = array_merge($this->arguments, $args); - } - } - } - } - - // Condition arguments - if(countobj($this->conditions) > 0) - { - foreach($this->conditions as $conditionGroup) - { - $args = $conditionGroup->getArguments(); - if(countobj($args) > 0) - { - $this->arguments = array_merge($this->arguments, $args); - } - } - } - - // Having arguments - if(countobj($this->having) > 0) - { - foreach($this->having as $conditionGroup) - { - $args = $conditionGroup->getArguments(); - if(countobj($args) > 0) - { - $this->arguments = array_merge($this->arguments, $args); - } - } - } - - // Navigation arguments - if(countobj($this->orderby) > 0) - { - foreach($this->orderby as $order) - { - $args = $order->getArguments(); - if(countobj($args) > 0) - { - $this->arguments = array_merge($this->arguments, $args); - } - } - } - } - - return $this->arguments; - } - -} -/* End of file Query.class.php */ -/* Location: ./classes/db/queryparts/Query.class.php */ diff --git a/classes/db/queryparts/Subquery.class.php b/classes/db/queryparts/Subquery.class.php deleted file mode 100644 index 2a1390f2f..000000000 --- a/classes/db/queryparts/Subquery.class.php +++ /dev/null @@ -1,80 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts - * @version 0.1 - */ -class Subquery extends Query -{ - - /** - * table alias - * @var string - */ - var $alias; - - /** - * join type - * @var string - */ - var $join_type; - - /** - * constructor - * @param string $alias - * @param string|array $columns - * @param string|array $tables - * @param string|array $conditions - * @param string|array $groups - * @param string|array $orderby - * @param int $limit - * @param string $join_type - * @return void - */ - function __construct($alias, $columns, $tables, $conditions, $groups, $orderby, $limit, $join_type = null) - { - $this->alias = $alias; - - $this->queryID = null; - $this->action = "select"; - - $this->columns = $columns; - $this->tables = $tables; - $this->conditions = $conditions; - $this->groups = $groups; - $this->orderby = $orderby; - $this->limit = $limit; - $this->join_type = $join_type; - } - - function getAlias() - { - return $this->alias; - } - - function isJoinTable() - { - if($this->join_type) - { - return true; - } - return false; - } - - function toString($with_values = true) - { - $oDB = &DB::getInstance(); - - return '(' . $oDB->getSelectSql($this, $with_values) . ')'; - } - - function isSubquery() - { - return true; - } - -} -/* End of file Subquery.class.php */ -/* Location: ./classes/db/queryparts/Subquery.class.php */ diff --git a/classes/db/queryparts/condition/Condition.class.php b/classes/db/queryparts/condition/Condition.class.php deleted file mode 100644 index 908c8fcd8..000000000 --- a/classes/db/queryparts/condition/Condition.class.php +++ /dev/null @@ -1,277 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/condition - * @version 0.1 - */ -class Condition -{ - - /** - * column name - * @var string - */ - var $column_name; - var $argument; - - /** - * operation can use 'equal', 'more', 'excess', 'less', 'below', 'like_tail', 'like_prefix', 'like', 'notlike_tail', - * 'notlike_prefix', 'notlike', 'in', 'notin', 'not_in', 'and', 'or', 'xor', 'not', 'notequal', 'between' - * 'null', 'notnull' - * @var string - */ - var $operation; - - /** - * pipe can use 'and', 'or'... - * @var string - */ - var $pipe; - var $_value; - var $_show; - var $_value_to_string; - - /** - * constructor - * @param string $column_name - * @param mixed $argument - * @param string $operation - * @param string $pipe - * @return void - */ - function __construct($column_name, $argument, $operation, $pipe = 'and') - { - $this->column_name = $column_name; - $this->argument = $argument; - $this->operation = $operation; - $this->pipe = $pipe; - } - - function getArgument() - { - return null; - } - - /** - * value to string - * @param boolean $withValue - * @return string - */ - function toString($withValue = true) - { - if(!isset($this->_value_to_string)) - { - if(!$this->show()) - { - $this->_value_to_string = ''; - } - else if($withValue) - { - $this->_value_to_string = $this->toStringWithValue(); - } - else - { - $this->_value_to_string = $this->toStringWithoutValue(); - } - } - return $this->_value_to_string; - } - - /** - * change string without value - * @return string - */ - function toStringWithoutValue() - { - return strtoupper($this->pipe) . ' ' . $this->getConditionPart($this->_value); - } - - /** - * change string with value - * @return string - */ - function toStringWithValue() - { - return strtoupper($this->pipe) . ' ' . $this->getConditionPart($this->_value); - } - - function setPipe($pipe) - { - $this->pipe = $pipe; - } - - /** - * @return boolean - */ - function show() - { - if(!isset($this->_show)) - { - if(is_array($this->_value) && count($this->_value) === 1 && $this->_value[0] === '') - { - $this->_show = false; - } - else - { - $this->_show = true; - switch($this->operation) - { - case 'equal' : - case 'more' : - case 'excess' : - case 'less' : - case 'below' : - case 'gte' : - case 'gt' : - case 'lte' : - case 'lt' : - case 'like_tail' : - case 'like_prefix' : - case 'like' : - case 'notlike_tail' : - case 'notlike_prefix' : - case 'notlike' : - case 'not_like' : - case 'regexp' : - case 'notregexp' : - case 'not_regexp' : - case 'in' : - case 'notin' : - case 'not_in' : - case 'and': - case 'or': - case 'xor': - case 'not': - case 'notequal' : - case 'not_equal' : - // if variable is not set or is not string or number, return - if(!isset($this->_value)) - { - $this->_show = false; - break; - } - if($this->_value === '') - { - $this->_show = false; - break; - } - $tmpArray = array('string' => 1, 'integer' => 1); - if(!isset($tmpArray[gettype($this->_value)])) - { - $this->_show = false; - break; - } - break; - case 'between' : - if(!is_array($this->_value)) - { - $this->_show = false; - break; - } - if(count($this->_value) != 2) - { - $this->_show = false; - break; - } - case 'null': - case 'notnull': - case 'not_null': - break; - default: - // If operation is not one of the above, means the condition is invalid - $this->_show = false; - } - } - } - return $this->_show; - } - - /** - * Return condition string - * @param int|string|array $value - * @return string - */ - function getConditionPart($value) - { - $name = $this->column_name; - $operation = $this->operation; - - switch($operation) - { - case 'equal' : - return $name . ' = ' . $value; - break; - case 'more' : - case 'gte' : - return $name . ' >= ' . $value; - break; - case 'excess' : - case 'gt' : - return $name . ' > ' . $value; - break; - case 'less' : - case 'lte' : - return $name . ' <= ' . $value; - break; - case 'below' : - case 'lt' : - return $name . ' < ' . $value; - break; - case 'like_tail' : - case 'like_prefix' : - case 'like' : - return $name . ' LIKE ' . $value; - case 'notlike_tail' : - case 'notlike_prefix' : - case 'notlike' : - case 'not_like' : - return $name . ' NOT LIKE ' . $value; - break; - case 'regexp' : - return $name . ' REGEXP ' . $value; - break; - case 'notregexp' : - case 'not_regexp' : - return $name . ' NOT REGEXP ' . $value; - break; - case 'in' : - return $name . ' IN ' . $value; - break; - case 'notin' : - case 'not_in' : - return $name . ' NOT IN ' . $value; - break; - case 'notequal' : - case 'not_equal' : - return $name . ' <> ' . $value; - break; - case 'notnull' : - case 'not_null' : - return $name . ' IS NOT NULL '; - break; - case 'null' : - return $name . ' IS NULL '; - break; - case 'and' : - return $name . ' & ' . $value; - break; - case 'or' : - return $name . ' | ' . $value; - break; - case 'xor' : - return $name . ' ^ ' . $value; - break; - case 'not' : - return $name . ' ~ ' . $value; - break; - case 'between' : - return $name . ' BETWEEN ' . $value[0] . ' AND ' . $value[1]; - break; - } - } - -} -/* End of file Condition.class.php */ -/* Location: ./classes/db/queryparts/condition/Condition.class.php */ diff --git a/classes/db/queryparts/condition/ConditionGroup.class.php b/classes/db/queryparts/condition/ConditionGroup.class.php deleted file mode 100644 index 2b6057ed8..000000000 --- a/classes/db/queryparts/condition/ConditionGroup.class.php +++ /dev/null @@ -1,133 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/condition - * @version 0.1 - */ -class ConditionGroup -{ - - /** - * condition list - * @var array - */ - var $conditions; - - /** - * pipe can use 'and', 'or'... - * @var string - */ - var $pipe; - var $_group; - var $_show; - - /** - * constructor - * @param array $conditions - * @param string $pipe - * @return void - */ - function __construct($conditions, $pipe = 'and') - { - $this->conditions = array(); - foreach($conditions as $condition) - { - if($condition->show()) - { - $this->conditions[] = $condition; - } - } - if(count($this->conditions) === 0) - { - $this->_show = false; - } - else - { - $this->_show = true; - } - - $this->pipe = $pipe; - } - - function show() - { - return $this->_show; - } - - function setPipe($pipe) - { - if($this->pipe !== $pipe) - { - $this->_group = null; - } - $this->pipe = $pipe; - } - - /** - * value to string - * @param boolean $with_value - * @return string - */ - function toString($with_value = true) - { - if(!isset($this->_group)) - { - $cond_indx = 0; - $group = ''; - - foreach($this->conditions as $condition) - { - if($cond_indx === 0) - { - $condition->setPipe(""); - } - $group .= $condition->toString($with_value) . ' '; - $cond_indx++; - } - - if($this->pipe !== "" && trim($group) !== '') - { - $group = strtoupper($this->pipe) . ' (' . $group . ')'; - } - - $this->_group = $group; - } - return $this->_group; - } - - /** - * return argument list - * @return array - */ - function getArguments() - { - $args = array(); - foreach($this->conditions as $condition) - { - if($condition instanceof ConditionGroup) - { - foreach($condition->getArguments() as $arg) - { - if($arg) - { - $args[] = $arg; - } - } - } - else - { - $arg = $condition->getArgument(); - if($arg) - { - $args[] = $arg; - } - } - } - return $args; - } - -} -/* End of file ConditionGroup.class.php */ -/* Location: ./classes/db/queryparts/condition/ConditionGroup.class.php */ diff --git a/classes/db/queryparts/condition/ConditionSubquery.class.php b/classes/db/queryparts/condition/ConditionSubquery.class.php deleted file mode 100644 index 60c2808d7..000000000 --- a/classes/db/queryparts/condition/ConditionSubquery.class.php +++ /dev/null @@ -1,28 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/condition - * @version 0.1 - */ -class ConditionSubquery extends Condition -{ - - /** - * constructor - * @param string $column_name - * @param mixed $argument - * @param string $operation - * @param string $pipe - * @return void - */ - function __construct($column_name, $argument, $operation, $pipe = "") - { - parent::__construct($column_name, $argument, $operation, $pipe); - $this->_value = $this->argument->toString(); - } - -} -/* End of file ConditionSubquery.class.php */ -/* Location: ./classes/db/queryparts/condition/ConditionSubquery.class.php */ diff --git a/classes/db/queryparts/condition/ConditionWithArgument.class.php b/classes/db/queryparts/condition/ConditionWithArgument.class.php deleted file mode 100644 index 9f42fd118..000000000 --- a/classes/db/queryparts/condition/ConditionWithArgument.class.php +++ /dev/null @@ -1,99 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/condition - * @version 0.1 - */ -class ConditionWithArgument extends Condition -{ - - /** - * constructor - * @param string $column_name - * @param mixed $argument - * @param string $operation - * @param string $pipe - * @return void - */ - function __construct($column_name, $argument, $operation, $pipe = "") - { - if($argument === null) - { - $this->_show = false; - return; - } - parent::__construct($column_name, $argument, $operation, $pipe); - $this->_value = $argument->getValue(); - } - - function getArgument() - { - if(!$this->show()) - return; - return $this->argument; - } - - /** - * change string without value - * @return string - */ - function toStringWithoutValue() - { - $value = $this->argument->getUnescapedValue(); - - if(is_array($value)) - { - $q = ''; - foreach($value as $v) - { - $q .= '?,'; - } - if($q !== '') - { - $q = substr($q, 0, -1); - } - $q = '(' . $q . ')'; - } - else - { - // Prepared statements: column names should not be sent as query arguments, but instead concatenated to query string - if($this->argument->isColumnName()) - { - $q = $value; - } - else - { - $q = '?'; - } - } - return strtoupper($this->pipe) . ' ' . $this->getConditionPart($q); - } - - /** - * @return boolean - */ - function show() - { - if(!isset($this->_show)) - { - if(!$this->argument->isValid()) - { - $this->_show = false; - } - if($this->_value === '\'\'') - { - $this->_show = false; - } - if(!isset($this->_show)) - { - return parent::show(); - } - } - return $this->_show; - } - -} -/* End of file ConditionWithArgument.class.php */ -/* Location: ./classes/db/queryparts/condition/ConditionWithArgument.class.php */ diff --git a/classes/db/queryparts/condition/ConditionWithoutArgument.class.php b/classes/db/queryparts/condition/ConditionWithoutArgument.class.php deleted file mode 100644 index 7b489d4a2..000000000 --- a/classes/db/queryparts/condition/ConditionWithoutArgument.class.php +++ /dev/null @@ -1,40 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/condition - * @version 0.1 - */ -class ConditionWithoutArgument extends Condition -{ - - /** - * constructor - * @param string $column_name - * @param mixed $argument - * @param string $operation - * @param string $pipe - * @return void - */ - function __construct($column_name, $argument, $operation, $pipe = "") - { - parent::__construct($column_name, $argument, $operation, $pipe); - $tmpArray = array('in' => 1, 'notin' => 1, 'not_in' => 1); - if(isset($tmpArray[$operation])) - { - if(is_array($argument)) - { - $argument = implode($argument, ','); - } - $this->_value = '(' . $argument . ')'; - } - else - { - $this->_value = $argument; - } - } - -} -/* End of file ConditionWithoutArgument.class.php */ -/* Location: ./classes/db/queryparts/condition/ConditionWithoutArgument.class.php */ diff --git a/classes/db/queryparts/expression/ClickCountExpression.class.php b/classes/db/queryparts/expression/ClickCountExpression.class.php deleted file mode 100644 index d91d66ccd..000000000 --- a/classes/db/queryparts/expression/ClickCountExpression.class.php +++ /dev/null @@ -1,62 +0,0 @@ - */ - -/** - * ClickCountExpression - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class ClickCountExpression extends SelectExpression -{ - - /** - * click count - * @var bool - */ - var $click_count; - - /** - * constructor - * @param string $column_name - * @param string $alias - * @param bool $click_count - * @return void - */ - function __construct($column_name, $alias = NULL, $click_count = false) - { - parent::__construct($column_name, $alias); - - if(!is_bool($click_count)) - { - // error_log("Click_count value for $column_name was not boolean", 0); - $this->click_count = false; - } - $this->click_count = $click_count; - } - - function show() - { - return $this->click_count; - } - - /** - * Return column expression, ex) column = column + 1 - * @return string - */ - function getExpression() - { - $db_type = Context::getDBType(); - if($db_type == 'cubrid') - { - return "INCR($this->column_name)"; - } - else - { - return "$this->column_name"; - } - } - -} -/* End of file ClickCountExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/ClickCountExpression.class.php */ diff --git a/classes/db/queryparts/expression/DeleteExpression.class.php b/classes/db/queryparts/expression/DeleteExpression.class.php deleted file mode 100644 index 24e93cf96..000000000 --- a/classes/db/queryparts/expression/DeleteExpression.class.php +++ /dev/null @@ -1,63 +0,0 @@ - */ - -/** - * DeleteExpression - * - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - * @todo Fix this class - */ -class DeleteExpression extends Expression -{ - - /** - * column value - * @var mixed - */ - var $value; - - /** - * constructor - * @param string $column_name - * @param mixed $value - * @return void - */ - function __construct($column_name, $value) - { - parent::__construct($column_name); - $this->value = $value; - } - - /** - * Return column expression, ex) column = value - * @return string - */ - function getExpression() - { - return "$this->column_name = $this->value"; - } - - function getValue() - { - // TODO Escape value according to column type instead of variable type - if(!is_numeric($this->value)) - { - return "'" . $this->value . "'"; - } - return $this->value; - } - - function show() - { - if(!$this->value) - { - return false; - } - return true; - } - -} -/* End of file DeleteExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/DeleteExpression.class.php */ diff --git a/classes/db/queryparts/expression/Expression.class.php b/classes/db/queryparts/expression/Expression.class.php deleted file mode 100644 index 7e29976da..000000000 --- a/classes/db/queryparts/expression/Expression.class.php +++ /dev/null @@ -1,56 +0,0 @@ - */ - -/** - * Expression - * Represents an expression used in select/update/insert/delete statements - * - * Examples (expressions are inside double square brackets): - * select [[columnA]], [[columnB as aliasB]] from tableA - * update tableA set [[columnA = valueA]] where columnB = something - * - * @author Corina - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class Expression -{ - - /** - * column name - * @var string - */ - var $column_name; - - /** - * constructor - * @param string $column_name - * @return void - */ - function __construct($column_name) - { - $this->column_name = $column_name; - } - - function getColumnName() - { - return $this->column_name; - } - - function show() - { - return false; - } - - /** - * Return column expression, ex) column as alias - * @return string - */ - function getExpression() - { - - } - -} -/* End of file Expression.class.php */ -/* Location: ./classes/db/queryparts/expression/Expression.class.php */ diff --git a/classes/db/queryparts/expression/InsertExpression.class.php b/classes/db/queryparts/expression/InsertExpression.class.php deleted file mode 100644 index fa64b1af2..000000000 --- a/classes/db/queryparts/expression/InsertExpression.class.php +++ /dev/null @@ -1,74 +0,0 @@ - */ - -/** - * InsertExpression - * - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class InsertExpression extends Expression -{ - - /** - * argument - * @var object - */ - var $argument; - - /** - * constructor - * @param string $column_name - * @param object $argument - * @return void - */ - function __construct($column_name, $argument) - { - parent::__construct($column_name); - $this->argument = $argument; - } - - function getValue($with_values = true) - { - if($with_values) - { - return $this->argument->getValue(); - } - return '?'; - } - - function show() - { - if(!$this->argument) - { - return false; - } - $value = $this->argument->getValue(); - if(!isset($value)) - { - return false; - } - return true; - } - - function getArgument() - { - return $this->argument; - } - - function getArguments() - { - if($this->argument) - { - return array($this->argument); - } - else - { - return array(); - } - } - -} -/* End of file InsertExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/InsertExpression.class.php */ diff --git a/classes/db/queryparts/expression/SelectExpression.class.php b/classes/db/queryparts/expression/SelectExpression.class.php deleted file mode 100644 index 78ea23408..000000000 --- a/classes/db/queryparts/expression/SelectExpression.class.php +++ /dev/null @@ -1,70 +0,0 @@ - */ - -/** - * SelectExpression - * Represents an expresion that appears in the select clause - * - * $column_name can be: - * - a table column name - * - an sql function - like count(*) - * - an sql expression - substr(column_name, 1, 8) or score1 + score2 - * $column_name is already escaped - * - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class SelectExpression extends Expression -{ - - /** - * column alias name - * @var string - */ - var $column_alias; - - /** - * constructor - * @param string $column_name - * @param string $alias - * @return void - */ - function __construct($column_name, $alias = NULL) - { - parent::__construct($column_name); - $this->column_alias = $alias; - } - - /** - * Return column expression, ex) column as alias - * @return string - */ - function getExpression() - { - return sprintf("%s%s", $this->column_name, $this->column_alias ? (' AS ' . $this->column_alias) : ""); - } - - function show() - { - return true; - } - - function getArgument() - { - return null; - } - - function getArguments() - { - return array(); - } - - function isSubquery() - { - return false; - } - -} -/* End of file SelectExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/SelectExpression.class.php */ diff --git a/classes/db/queryparts/expression/StarExpression.class.php b/classes/db/queryparts/expression/StarExpression.class.php deleted file mode 100644 index 3318d9543..000000000 --- a/classes/db/queryparts/expression/StarExpression.class.php +++ /dev/null @@ -1,37 +0,0 @@ - */ - -/** - * StarExpression - * Represents the * in 'select * from ...' statements - * - * @author Corina - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class StarExpression extends SelectExpression -{ - - /** - * constructor, set the column to asterisk - * @return void - */ - function __construct() - { - parent::__construct("*"); - } - - function getArgument() - { - return null; - } - - function getArguments() - { - // StarExpression has no arguments - return array(); - } - -} -/* End of file StarExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/StarExpression.class.php */ diff --git a/classes/db/queryparts/expression/UpdateExpression.class.php b/classes/db/queryparts/expression/UpdateExpression.class.php deleted file mode 100644 index ffe8146bf..000000000 --- a/classes/db/queryparts/expression/UpdateExpression.class.php +++ /dev/null @@ -1,119 +0,0 @@ - */ - -/** - * UpdateExpression - * - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class UpdateExpression extends Expression -{ - - /** - * argument - * @var object - */ - var $argument; - - /** - * constructor - * @param string $column_name - * @param object $argument - * @return void - */ - function __construct($column_name, $argument) - { - parent::__construct($column_name); - $this->argument = $argument; - } - - /** - * Return column expression, ex) column = value - * @return string - */ - function getExpression($with_value = true) - { - if($with_value) - { - return $this->getExpressionWithValue(); - } - return $this->getExpressionWithoutValue(); - } - - /** - * Return column expression, ex) column = value - * @return string - */ - function getExpressionWithValue() - { - $value = $this->argument->getValue(); - $operation = $this->argument->getColumnOperation(); - if(isset($operation)) - { - return "$this->column_name = $this->column_name $operation $value"; - } - return "$this->column_name = $value"; - } - - /** - * Return column expression, ex) column = ? - * Can use prepare statement - * @return string - */ - function getExpressionWithoutValue() - { - $operation = $this->argument->getColumnOperation(); - if(isset($operation)) - { - return "$this->column_name = $this->column_name $operation ?"; - } - return "$this->column_name = ?"; - } - - function getValue() - { - // TODO Escape value according to column type instead of variable type - $value = $this->argument->getValue(); - if(!is_numeric($value)) - { - return "'" . $value . "'"; - } - return $value; - } - - function show() - { - if(!$this->argument) - { - return false; - } - $value = $this->argument->getValue(); - if(!isset($value)) - { - return false; - } - return true; - } - - function getArgument() - { - return $this->argument; - } - - function getArguments() - { - if($this->argument) - { - return array($this->argument); - } - else - { - return array(); - } - } - -} -/* End of file UpdateExpression.class.php */ -/* Location: ./classes/db/queryparts/expression/UpdateExpression.class.php */ diff --git a/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php b/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php deleted file mode 100644 index 5c025162e..000000000 --- a/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php +++ /dev/null @@ -1,74 +0,0 @@ - */ - -/** - * UpdateExpression - * - * @author Arnia Software - * @package /classes/db/queryparts/expression - * @version 0.1 - */ -class UpdateExpressionWithoutArgument extends UpdateExpression -{ - - /** - * argument - * @var object - */ - var $argument; - - /** - * constructor - * @param string $column_name - * @param object $argument - * @return void - */ - function __construct($column_name, $argument) - { - parent::__construct($column_name, $argument); - $this->argument = $argument; - } - - function getExpression($with_value = true) - { - return "$this->column_name = $this->argument"; - } - - function getValue() - { - // TODO Escape value according to column type instead of variable type - $value = $this->argument; - if(!is_numeric($value)) - { - return "'" . $value . "'"; - } - return $value; - } - - function show() - { - if(!$this->argument) - { - return false; - } - $value = $this->argument; - if(!isset($value)) - { - return false; - } - return true; - } - - function getArgument() - { - return null; - } - - function getArguments() - { - return array(); - } - -} -/* End of file UpdateExpressionWithoutArgument.class.php */ -/* Location: ./classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php */ diff --git a/classes/db/queryparts/limit/Limit.class.php b/classes/db/queryparts/limit/Limit.class.php deleted file mode 100644 index 6d6e851bf..000000000 --- a/classes/db/queryparts/limit/Limit.class.php +++ /dev/null @@ -1,101 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/limit - * @version 0.1 - */ -class Limit -{ - - /** - * start number - * @var int - */ - var $start; - - /** - * list count - * @var int - */ - var $list_count; - - /** - * page count - * @var int - */ - var $page_count; - - /** - * current page - * @var int - */ - var $page; - - /** - * constructor - * @param int $list_count - * @param int $page - * @param int $page_count - * @param int $offset - * @return void - */ - function __construct($list_count, $page = NULL, $page_count = NULL, $offset = NULL) - { - $this->list_count = $list_count; - if($list_count->getValue()) - { - if($page && $page->getValue()) - { - $this->start = ($page->getValue() - 1) * $list_count->getValue(); - $this->page_count = $page_count; - $this->page = $page; - } - elseif($offset) - { - $this->start = $offset->getValue(); - } - } - } - - /** - * In case you choose to use query limit in other cases than page select - * @return boolean - */ - function isPageHandler() - { - if($this->page) - { - return true; - } - else - { - return false; - } - } - - function getOffset() - { - return $this->start; - } - - function getLimit() - { - return $this->list_count->getValue(); - } - - function toString() - { - if($this->start) - { - return $this->start . ' , ' . $this->list_count->getValue(); - } - else - { - return $this->list_count->getValue() ?: ''; - } - } -} -/* End of file Limit.class.php */ -/* Location: ./classes/db/limit/Limit.class.php */ diff --git a/classes/db/queryparts/order/OrderByColumn.class.php b/classes/db/queryparts/order/OrderByColumn.class.php deleted file mode 100644 index 7a6a67e08..000000000 --- a/classes/db/queryparts/order/OrderByColumn.class.php +++ /dev/null @@ -1,74 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/order - * @version 0.1 - */ -class OrderByColumn -{ - - /** - * column name - * @var string - */ - var $column_name; - - /** - * sort order - * @var string - */ - var $sort_order; - - /** - * constructor - * @param string $column_name - * @param string $sort_order - * @return void - */ - function __construct($column_name, $sort_order) - { - $this->column_name = $column_name; - $this->sort_order = $sort_order; - } - - function toString() - { - $result = $this->getColumnName(); - $result .= ' '; - $result .= is_a($this->sort_order, 'Argument') ? $this->sort_order->getValue() : strtoupper($this->sort_order); - return $result; - } - - function getColumnName() - { - return is_a($this->column_name, 'Argument') ? $this->column_name->getValue() : $this->column_name; - } - - function getPureColumnName() - { - return is_a($this->column_name, 'Argument') ? $this->column_name->getPureValue() : $this->column_name; - } - - function getPureSortOrder() - { - return is_a($this->sort_order, 'Argument') ? $this->sort_order->getPureValue() : $this->sort_order; - } - - function getArguments() - { - $args = array(); - if(is_a($this->column_name, 'Argument')) - { - $args[] = $this->column_name; - } - if(is_a($this->sort_order, 'Argument')) - { - $args[] = $this->sort_order; - } - } - -} -/* End of file OrderByColumn.class.php */ -/* Location: ./classes/db/order/OrderByColumn.class.php */ diff --git a/classes/db/queryparts/table/IndexHint.class.php b/classes/db/queryparts/table/IndexHint.class.php deleted file mode 100644 index 4b80ecf97..000000000 --- a/classes/db/queryparts/table/IndexHint.class.php +++ /dev/null @@ -1,48 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/table - * @version 0.1 - */ -class IndexHint -{ - - /** - * index name - * @var string - */ - var $index_name; - - /** - * index hint type, ex) IGNORE, FORCE, USE... - * @var string - */ - var $index_hint_type; - - /** - * constructor - * @param string $index_name - * @param string $index_hint_type - * @return void - */ - function __construct($index_name, $index_hint_type) - { - $this->index_name = $index_name; - $this->index_hint_type = $index_hint_type; - } - - function getIndexName() - { - return $this->index_name; - } - - function getIndexHintType() - { - return $this->index_hint_type; - } - -} -/* End of file IndexHint.class.php */ -/* Location: ./classes/db/queryparts/table/IndexHint.class.php */ diff --git a/classes/db/queryparts/table/JoinTable.class.php b/classes/db/queryparts/table/JoinTable.class.php deleted file mode 100644 index 6e882cc28..000000000 --- a/classes/db/queryparts/table/JoinTable.class.php +++ /dev/null @@ -1,78 +0,0 @@ - */ - -/** - * class JoinTable - * $conditions in an array of Condition objects - * - * @author Arnia Software - * @package /classes/db/queryparts/table - * @version 0.1 - */ -class JoinTable extends Table -{ - - /** - * join type - * @var string - */ - var $join_type; - - /** - * condition list - * @var array - */ - var $conditions; - - /** - * constructor - * @param string $name - * @param string $alias - * @param string $join_type - * @param array $conditions - * @return void - */ - function __construct($name, $alias, $join_type, $conditions) - { - parent::__construct($name, $alias); - $this->join_type = $join_type; - $this->conditions = $conditions; - } - - function toString($with_value = true) - { - $part = strtoupper($this->join_type) . ' ' . $this->name; - $part .= $this->alias ? (' AS ' . $this->alias) : ''; - $part .= ' ON '; - - $condition_count = 0; - foreach($this->conditions as $conditionGroup) - { - if($condition_count === 0) - { - $conditionGroup->setPipe(""); - } - $part .= $conditionGroup->toString($with_value); - $condition_count++; - } - return $part; - } - - function isJoinTable() - { - return true; - } - - function getArguments() - { - $args = array(); - foreach($this->conditions as $conditionGroup) - { - $args = array_merge($args, $conditionGroup->getArguments()); - } - return $args; - } - -} -/* End of file JoinTable.class.php */ -/* Location: ./classes/db/queryparts/table/JoinTable.class.php */ diff --git a/classes/db/queryparts/table/MysqlTableWithHint.class.php b/classes/db/queryparts/table/MysqlTableWithHint.class.php deleted file mode 100644 index 1642ad7aa..000000000 --- a/classes/db/queryparts/table/MysqlTableWithHint.class.php +++ /dev/null @@ -1,83 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/table - * @version 0.1 - */ -class MysqlTableWithHint extends Table -{ - - /** - * table name - * @var string - */ - var $name; - - /** - * table alias - * @var string - */ - var $alias; - - /** - * index hint type, ex) IGNORE, FORCE, USE... - * @var array - */ - var $index_hints_list; - - /** - * constructor - * @param string $name - * @param string $alias - * @param string $index_hints_list - * @return void - */ - function __construct($name, $alias = NULL, $index_hints_list) - { - parent::__construct($name, $alias); - $this->index_hints_list = $index_hints_list; - } - - function toString() - { - $result = parent::toString(); - - $use_index_hint = ''; - $force_index_hint = ''; - $ignore_index_hint = ''; - foreach($this->index_hints_list as $index_hint) - { - $index_hint_type = $index_hint->getIndexHintType(); - if($index_hint_type == 'USE') - { - $use_index_hint .= $index_hint->getIndexName() . ', '; - } - else if($index_hint_type == 'FORCE') - { - $force_index_hint .= $index_hint->getIndexName() . ', '; - } - else if($index_hint_type == 'IGNORE') - { - $ignore_index_hint .= $index_hint->getIndexName() . ', '; - } - } - if($use_index_hint != '') - { - $result .= ' USE INDEX (' . substr($use_index_hint, 0, -2) . ') '; - } - if($force_index_hint != '') - { - $result .= ' FORCE INDEX (' . substr($force_index_hint, 0, -2) . ') '; - } - if($ignore_index_hint != '') - { - $result .= ' IGNORE INDEX (' . substr($ignore_index_hint, 0, -2) . ') '; - } - return $result; - } - -} -/* End of file MysqlTableWithHint.class.php */ -/* Location: ./classes/db/queryparts/table/MysqlTableWithHint.class.php */ diff --git a/classes/db/queryparts/table/Table.class.php b/classes/db/queryparts/table/Table.class.php deleted file mode 100644 index 6c0a17c69..000000000 --- a/classes/db/queryparts/table/Table.class.php +++ /dev/null @@ -1,59 +0,0 @@ - */ - -/** - * @author NAVER (developers@xpressengine.com) - * @package /classes/db/queryparts/table - * @version 0.1 - */ -class Table -{ - - /** - * table name - * @var string - */ - var $name; - - /** - * table alias - * @var string - */ - var $alias; - - /** - * constructor - * @param string $name - * @param string $alias - * @return void - */ - function __construct($name, $alias = NULL) - { - $this->name = $name; - $this->alias = $alias; - } - - function toString() - { - //return $this->name; - return sprintf("%s%s", $this->name, $this->alias ? (' AS ' . $this->alias) : ''); - } - - function getName() - { - return $this->name; - } - - function getAlias() - { - return $this->alias; - } - - function isJoinTable() - { - return false; - } - -} -/* End of file Table.class.php */ -/* Location: ./classes/db/queryparts/table/Table.class.php */ diff --git a/classes/xml/XmlQueryParser.class.php b/classes/xml/XmlQueryParser.class.php deleted file mode 100644 index 66284031a..000000000 --- a/classes/xml/XmlQueryParser.class.php +++ /dev/null @@ -1,114 +0,0 @@ - */ - -/** - * File containing the XE 1.5 XmlQueryParserClass - */ -if(!defined('__XE_LOADED_XML_CLASS__')) -{ - define('__XE_LOADED_XML_CLASS__', 1); -} - -/** - * New XmlQueryParser class
- * Parses XE XML query files - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml - * @version 0.1 - */ -class XmlQueryParser extends XmlParser -{ - - /** - * constructor - * @return void - */ - function __construct() - { - - } - - /** - * Create XmlQueryParser instance for Singleton - * - * @return XmlQueryParser object - */ - function &getInstance() - { - static $theInstance = NULL; - if(!isset($theInstance)) - { - $theInstance = new XmlQueryParser(); - } - return $theInstance; - } - - /** - * Parses an XML query file - * - * 1. Read xml file
- * 2. Check the action
- * 3. Parse and write cache file
- * - * @param $query_id - * @param $xml_file - * @param $cache_file - * - * @return QueryParser object - */ - function &parse_xml_query($query_id, $xml_file, $cache_file) - { - // Read xml file - $xml_obj = $this->getXmlFileContent($xml_file); - - // insert, update, delete, select action - $action = strtolower($xml_obj->query->attrs->action); - if(!$action) - { - return; - } - - // Write query cache file - $parser = new QueryParser($xml_obj->query); - FileHandler::writeFile($cache_file, $parser->toString()); - - return $parser; - } - - /** - * Override for parent "parse" method - * - * @param null $query_id - * @param null $xml_file - * @param null $cache_file - * - * @return void - */ - function parse($query_id = NULL, $xml_file = NULL, $cache_file = NULL) - { - $this->parse_xml_query($query_id, $xml_file, $cache_file); - } - - /** - * Returns XML file contents as an object - * or NULL in case of error - * - * @param $xml_file - * @return array|NULL - */ - function getXmlFileContent($xml_file) - { - $buff = FileHandler::readFile($xml_file); - $xml_obj = parent::parse($buff); - if(!$xml_obj) - { - return; - } - unset($buff); - return $xml_obj; - } - -} -/* End of file XmlQueryParser.class.php */ -/* Location: ./classes/xml/XmlQueryParser.class.php */ diff --git a/classes/xml/xmlquery/DBParser.class.php b/classes/xml/xmlquery/DBParser.class.php deleted file mode 100644 index 0fd4d46ee..000000000 --- a/classes/xml/xmlquery/DBParser.class.php +++ /dev/null @@ -1,333 +0,0 @@ - */ - -/** - * File containing the DBParser class - */ - -/** - * Escapes query statements:
- * - column names: member.member_srl => "member"."member_srl"
- * - expressions: SUM(member.member_srl) => SUM("member"."member_srl")
- * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery - * @version 0.1 - */ -class DBParser -{ - - /** - * Character for escape target value on the left - * - * For example, in CUBRID left and right escape - * chars are the same, the double quote - "
- * But for SQL Server, the escape is made with - * [double brackets], so the left and right char differ - * - * - * @var string - */ - var $escape_char_left; - - /** - * Character for escape target value on the right - * - * For example, in CUBRID left and right escape - * chars are the same, the double quote - "
- * But for SQL Server, the escape is made with - * [double brackets], so the left and right char differ - * - * @var string - */ - var $escape_char_right; - - /** - * Table prefix string - * - * Default is "xe_" - * - * @var string - */ - var $table_prefix; - - /** - * Constructor - * - * @param string $escape_char_left - * @param string $escape_char_right - * @param string $table_prefix - * - * @return void - */ - function __construct($escape_char_left, $escape_char_right = "", $table_prefix = "xe_") - { - $this->escape_char_left = $escape_char_left; - if($escape_char_right !== "") - { - $this->escape_char_right = $escape_char_right; - } - else - { - $this->escape_char_right = $escape_char_left; - } - $this->table_prefix = $table_prefix; - } - - /** - * Get escape character - * - * @param string $leftOrRight left or right - * @return string - */ - function getEscapeChar($leftOrRight) - { - if($leftOrRight === 'left') - { - return $this->escape_char_left; - } - else - { - return $this->escape_char_right; - } - } - - /** - * Escape the value - * - * @param mixed $name - * @return string - */ - function escape($name) - { - return $this->escape_char_left . $name . $this->escape_char_right; - } - - /** - * Escape the string value - * - * @param string $name - * @return string - */ - function escapeString($name) - { - return "'" . $this->escapeStringValue($name) . "'"; - } - - /** - * Escape the string value - * - * @param string $value - * @return string - */ - function escapeStringValue($value) - { - if($value == "*") - { - return $value; - } - if(is_string($value)) - { - return $value = str_replace("'", "''", $value); - } - return $value; - } - - /** - * Return table full name - * - * @param string $name table name without table prefix - * - * @return string table full name with table prefix - */ - function parseTableName($name) - { - return $this->table_prefix . $name; - } - - /** - * Return column name after escape - * - * @param string $name column name before escape - * - * @return string column name after escape - */ - function parseColumnName($name) - { - return $this->escapeColumn($name); - } - - /** - * Escape column name - * - * @param string $column_name - * @return string column name with db name - */ - function escapeColumn($column_name) - { - if($this->isUnqualifiedColumnName($column_name)) - { - return $this->escape($column_name); - } - if($this->isQualifiedColumnName($column_name)) - { - list($table, $column) = explode('.', $column_name); - // $table can also be an alias, so the prefix should not be added - return $this->escape($table) . '.' . $this->escape($column); - //return $this->escape($this->parseTableName($table)).'.'.$this->escape($column); - } - } - - /** - * Checks to see if a given column name is unqualified - * - * Ex: "member_srl" -> unqualified
- * "member"."member_srl" -> qualified - * - * @param string $column_name - * @return bool - */ - function isUnqualifiedColumnName($column_name) - { - if(strpos($column_name, '.') === FALSE && strpos($column_name, '(') === FALSE) - { - return TRUE; - } - return FALSE; - } - - /** - * Checks to see if a given column name is qualified - * - * Ex: "member_srl" -> unqualified
- * "member"."member_srl" -> qualified - * - * @param string $column_name - * @return bool - */ - function isQualifiedColumnName($column_name) - { - if(strpos($column_name, '.') !== FALSE && strpos($column_name, '(') === FALSE) - { - return TRUE; - } - return FALSE; - } - - /** - * Escapes a query expression - * - * An expression can be:
- *
    - *
  • a column name: "member_srl" or "xe_member"."member_srl" - *
  • an expression: - *
      - *
    • LEFT(UPPER("content"))
      - *
    • readed_count + voted_count
      - *
    • CAST(regdate as DATE)
    • - *
    - *
  • - *
- * - * @param $column_name - * @return string - */ - function parseExpression($column_name) - { - $functions = preg_split('/([\+\-\*\/\ ])/', $column_name, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - foreach($functions as $k => $v) - { - $function = &$functions[$k]; - if(strlen($function) == 1) - { - continue; // skip delimiters - } - $pos = strrpos("(", $function); - $matches = preg_split('/([a-zA-Z0-9_*]+)/', $function, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $total_brackets = substr_count($function, "("); - $brackets = 0; - foreach($matches as $i => $j) - { - $match = &$matches[$i]; - if($match == '(') - { - $brackets++; - continue; - } - if(strpos($match, ')') !== FALSE) - { - continue; - } - if(in_array($match, array(',', '.'))) - { - continue; - } - if($brackets == $total_brackets) - { - if(!is_numeric($match) && !in_array(strtoupper($match), array('UNSIGNED', 'INTEGER', 'AS')) && !preg_match('/^[A-Z]+$/', $match)) - { - $match = $this->escapeColumnExpression($match); - } - } - } - $function = implode('', $matches); - } - return implode('', $functions); - } - - /** - * Checks if a given argument is an asterisk - * - * @param string $column_name - * @return bool - */ - function isStar($column_name) - { - if(substr($column_name, -1) == '*') - { - return TRUE; - } - return FALSE; - } - - /** - * Checks to see if expression is an aggregate star function - * like count(*) - * - * @param string $column_name - * @return bool - */ - function isStarFunction($column_name) - { - if(strpos($column_name, "(*)") !== FALSE) - { - return TRUE; - } - return FALSE; - } - - /** - * Return column name after escape - * @param string $column_name - * @return string - */ - function escapeColumnExpression($column_name) - { - if($this->isStar($column_name)) - { - return $column_name; - } - if($this->isStarFunction($column_name)) - { - return $column_name; - } - if(stripos($column_name, 'distinct') !== FALSE) - { - return $column_name; - } - return $this->escapeColumn($column_name); - } - -} -/* End of file DBParser.class.php */ -/* Location: ./classes/xml/xmlquery/DBParser.class.php */ diff --git a/classes/xml/xmlquery/QueryParser.class.php b/classes/xml/xmlquery/QueryParser.class.php deleted file mode 100644 index 4fe996d1f..000000000 --- a/classes/xml/xmlquery/QueryParser.class.php +++ /dev/null @@ -1,127 +0,0 @@ - */ - -/** - * File containing the QueryParser class - */ - -/** - * Parses an XML Object and returns a string used for generating the PHP cache file
- * The XML Object structure must be the one defined in the XmlParser class - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery - * @version 0.1 - */ -class QueryParser -{ - - /** - * Property containing the associated QueryTag object - * - * @var QueryTag object - */ - var $queryTag; - - /** - * Constructor - * - * @param object $query XML object obtained after reading the XML Query file - * @param bool $isSubQuery - * @return void - */ - function __construct($query = NULL, $isSubQuery = FALSE) - { - if($query) - { - $this->queryTag = new QueryTag($query, $isSubQuery); - } - } - - /** - * Returns table information - * - * Used for finding column type info (string/numeric)
- * Obtains the table info from XE's XML schema files - * - * @param object $query_id - * @param bool $table_name - * @return array - */ - function getTableInfo($query_id, $table_name) - { - $column_type = array(); - $module = ''; - - $id_args = explode('.', $query_id); - if(count($id_args) == 2) - { - $target = 'modules'; - $module = $id_args[0]; - $id = $id_args[1]; - } - else if(count($id_args) == 3) - { - $target = $id_args[0]; - $targetList = array('modules' => 1, 'addons' => 1, 'widgets' => 1); - if(!isset($targetList[$target])) - { - return; - } - $module = $id_args[1]; - $id = $id_args[2]; - } - - // get column properties from the table - $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $module, $table_name); - if(!file_exists($table_file)) - { - $searched_list = FileHandler::readDir(_XE_PATH_ . 'modules'); - $searched_count = count($searched_list); - for($i = 0; $i < $searched_count; $i++) - { - $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $searched_list[$i], $table_name); - if(file_exists($table_file)) - { - break; - } - } - } - - if(file_exists($table_file)) - { - $table_xml = FileHandler::readFile($table_file); - $xml_parser = new XmlParser(); - $table_obj = $xml_parser->parse($table_xml); - if($table_obj->table) - { - if(isset($table_obj->table->column) && !is_array($table_obj->table->column)) - { - $table_obj->table->column = array($table_obj->table->column); - } - - foreach($table_obj->table->column as $k => $v) - { - $column_type[$v->attrs->name] = $v->attrs->type; - } - } - } - - return $column_type; - } - - /** - * Returns the contents for the query cache file - * - * @return string - */ - function toString() - { - return "queryTag->toString() - . 'return $query; ?>'; - } - -} -/* End of file QueryParser.class.php */ -/* Location: ./classes/xml/xmlquery/QueryParser.class.php */ diff --git a/classes/xml/xmlquery/argument/Argument.class.php b/classes/xml/xmlquery/argument/Argument.class.php deleted file mode 100644 index b37aa91da..000000000 --- a/classes/xml/xmlquery/argument/Argument.class.php +++ /dev/null @@ -1,396 +0,0 @@ - */ - -/** - * Argument class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/argument - * @version 0.1 - */ -class Argument -{ - - /** - * argument value - * @var mixed - */ - var $value; - - /** - * argument name - * @var string - */ - var $name; - - /** - * argument type - * @var string - */ - var $type; - - /** - * result of argument type check - * @var bool - */ - var $isValid; - - /** - * error message - * @var BaseObject - */ - var $errorMessage; - - /** - * column operation - */ - var $column_operation; - - /** - * Check if arg value is user submnitted or default - * @var mixed - */ - var $uses_default_value; - - /** - * Caches escaped and toString value so that the parsing won't happen multiple times - * @var mixed - */ - var $_value; // - - /** - * constructor - * @param string $name - * @param mixed $value - * @return void - */ - - function __construct($name, $value) - { - $this->value = $value; - $this->name = $name; - $this->isValid = TRUE; - } - - function getType() - { - if(isset($this->type)) - { - return $this->type; - } - if(is_string($this->value)) - { - return 'column_name'; - } - - return 'number'; - } - - function setColumnType($value) - { - $this->type = $value; - } - - function setColumnOperation($operation) - { - $this->column_operation = $operation; - } - - function getName() - { - return $this->name; - } - - function getValue() - { - if(!isset($this->_value)) - { - $value = $this->getEscapedValue(); - $this->_value = $this->toString($value); - } - return $this->_value; - } - - function getPureValue() - { - return $this->value; - } - - function getColumnOperation() - { - return $this->column_operation; - } - - function getEscapedValue() - { - return $this->escapeValue($this->value); - } - - function getUnescapedValue() - { - if($this->value === 'null') - { - return null; - } - return $this->value; - } - - /** - * mixed value to string - * @param mixed $value - * @return string - */ - function toString($value) - { - if(is_array($value)) - { - if(count($value) === 0) - { - return ''; - } - if(count($value) === 1 && $value[0] === '') - { - return ''; - } - return '(' . implode(',', $value) . ')'; - } - return $value; - } - - /** - * escape value - * @param mixed $value - * @return mixed - */ - function escapeValue($value) - { - $column_type = $this->getType(); - if($column_type == 'column_name') - { - $dbParser = DB::getParser(); - return $dbParser->parseExpression($value); - } - if(!isset($value)) - { - return null; - } - - $columnTypeList = array('date' => 1, 'varchar' => 1, 'char' => 1, 'text' => 1, 'bigtext' => 1); - if(isset($columnTypeList[$column_type])) - { - if(!is_array($value)) - { - $value = $this->_escapeStringValue($value); - } - else - { - foreach($value as $key=>$val) - { - $value[$key] = $this->_escapeStringValue($val); - } - } - } - if($this->uses_default_value) - { - return $value; - } - if($column_type == 'number') - { - if(is_array($value)) - { - foreach($value AS $key => $val) - { - if(isset($val) && $val !== '') - { - $value[$key] = (int) $val; - } - } - } - else - { - $value = (int) $value; - } - } - - return $value; - } - - /** - * escape string value - * @param string $value - * @return string - */ - function _escapeStringValue($value) - { - // Remove non-utf8 chars. - $regex = '@((?:[\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}){1,100})|([\xF0-\xF7][\x80-\xBF]{3})|([\x80-\xBF])|([\xC0-\xFF])@x'; - - $value = preg_replace_callback($regex, array($this, 'utf8Replacer'), $value); - $db = DB::getInstance(); - $value = $db->addQuotes($value); - return '\'' . $value . '\''; - } - - function utf8Replacer($captures) - { - if(strlen($captures[1])) - { - // Valid byte sequence. Return unmodified. - return $captures[1]; - } - else if(strlen($captures[2])) - { - // Remove user defined area - if("\xF3\xB0\x80\x80" <= $captures[2]) - { - return; - } - - return $captures[2]; - } - else - { - return; - } - } - - function isValid() - { - return $this->isValid; - } - - function isColumnName() - { - $type = $this->getType(); - $value = $this->getUnescapedValue(); - if($type == 'column_name') - { - return TRUE; - } - if($type == 'number' && is_null($value)) - { - return FALSE; - } - if($type == 'number' && !is_numeric($value) && $this->uses_default_value) - { - return TRUE; - } - return FALSE; - } - - function getErrorMessage() - { - return $this->errorMessage; - } - - function ensureDefaultValue($default_value) - { - if($this->value === NULL || $this->value === '') - { - $this->value = $default_value; - $this->uses_default_value = TRUE; - } - } - - /** - * check filter by filter type - * @param string $filter_type - * @return void - */ - function checkFilter($filter_type) - { - if(isset($this->value) && $this->value != '') - { - global $lang; - $val = $this->value; - $key = $this->name; - switch($filter_type) - { - case 'email' : - case 'email_address' : - if(!preg_match('/^[\w-]+((?:\.|\+|\~)[\w-]+)*@[\w-]+(\.[\w-]+)+$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_email, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - case 'homepage' : - if(!preg_match('/^(http|https)+(:\/\/)+[0-9a-z_-]+\.[^ ]+$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_homepage, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - case 'userid' : - case 'user_id' : - if(!preg_match('/^[a-zA-Z]+([_0-9a-zA-Z]+)*$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_userid, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - case 'number' : - case 'numbers' : - if(is_array($val)) - { - $val = join(',', $val); - } - if(!preg_match('/^(-?)[0-9]+(,\-?[0-9]+)*$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_number, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - case 'alpha' : - if(!preg_match('/^[a-z]+$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_alpha, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - case 'alpha_number' : - if(!preg_match('/^[0-9a-z]+$/is', $val)) - { - $this->isValid = FALSE; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->invalid_alpha_number, $lang->{$key} ? $lang->{$key} : $key)); - } - break; - } - } - } - - function checkMaxLength($length) - { - if($this->value && (strlen($this->value) > $length)) - { - global $lang; - $this->isValid = FALSE; - $key = $this->name; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->outofrange, $lang->{$key} ? $lang->{$key} : $key)); - } - } - - function checkMinLength($length) - { - if($this->value && (strlen($this->value) < $length)) - { - global $lang; - $this->isValid = FALSE; - $key = $this->name; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->outofrange, $lang->{$key} ? $lang->{$key} : $key)); - } - } - - function checkNotNull() - { - if(!isset($this->value)) - { - global $lang; - $this->isValid = FALSE; - $key = $this->name; - $this->errorMessage = new BaseObject(-1, sprintf($lang->filter->isnull, $lang->{$key} ? $lang->{$key} : $key)); - } - } - -} -/* End of file Argument.class.php */ -/* Location: ./classes/xml/xmlquery/argument/Argument.class.php */ diff --git a/classes/xml/xmlquery/argument/ConditionArgument.class.php b/classes/xml/xmlquery/argument/ConditionArgument.class.php deleted file mode 100644 index be97fc097..000000000 --- a/classes/xml/xmlquery/argument/ConditionArgument.class.php +++ /dev/null @@ -1,153 +0,0 @@ - */ - -/** - * ConditionArgument class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/argument - * @version 0.1 - */ -class ConditionArgument extends Argument -{ - - /** - * Operator keyword. for example 'in', 'notint', 'between' - * @var string - */ - var $operation; - - /** - * constructor - * @param string $name - * @param mixed $value - * @param string $operation - * @return void - */ - function __construct($name, $value, $operation) - { - $operationList = array('in' => 1, 'notin' => 1, 'not_in' => 1, 'between' => 1); - if(isset($value) && isset($operationList[$operation]) && !is_array($value) && $value != '') - { - $value = str_replace(' ', '', $value); - $value = str_replace('\'', '', $value); - $value = explode(',', $value); - } - parent::__construct($name, $value); - $this->operation = $operation; - } - - /** - * create condition value. set $this->value - * @return void - */ - function createConditionValue() - { - if(!isset($this->value)) - { - return; - } - - $operation = $this->operation; - $value = $this->value; - - switch($operation) - { - case 'like_prefix' : - if(defined('__CUBRID_VERSION__') && __CUBRID_VERSION__ >= '8.4.1') - { - $this->value = '^' . str_replace('%', '(.*)', preg_quote($value)); - } - else - { - $this->value = $value . '%'; - } - break; - case 'like_tail' : - if(defined('__CUBRID_VERSION__') && __CUBRID_VERSION__ >= '8.4.1') - { - $this->value = str_replace('%', '(.*)', preg_quote($value)) . '$'; - } - else - { - $this->value = '%' . $value; - } - break; - case 'like' : - if(defined('__CUBRID_VERSION__') && __CUBRID_VERSION__ >= '8.4.1') - { - $this->value = str_replace('%', '(.*)', preg_quote($value)); - } - else - { - $this->value = '%' . $value . '%'; - } - break; - case 'notlike' : - $this->value = '%' . $value . '%'; - break; - case 'notlike_prefix' : - $this->value = $value . '%'; - break; - case 'notlike_tail' : - $this->value = '%' . $value; - break; - case 'in': - if(!is_array($value)) - { - $this->value = array($value); - } - break; - case 'notin': - case 'not_in': - if(!is_array($value)) - { - $this->value = array($value); - } - break; - } - } - - /** - * Since ConditionArgument is used in WHERE clause, - * where the argument value is compared to a table column, - * it is assumed that all arguments have type. There are cases though - * where the column does not have any type - if it was removed from - * the XML schema for example - see the is_secret column in xe_documents table. - * In this case, the column type is retrieved according to argument - * value type (using the PHP function is_numeric). - * - * @return type string - */ - function getType() - { - if($this->type) - { - return $this->type; - } - else if(!is_numeric($this->value)) - { - return 'varchar'; - } - else - { - return ''; - } - } - - function setColumnType($column_type) - { - if(!isset($this->value)) - { - return; - } - if($column_type === '') - { - return; - } - - $this->type = $column_type; - } - -} -/* End of file ConditionArgument.class.php */ -/* Location: ./classes/xml/xmlquery/argument/ConditionArgument.class.php */ diff --git a/classes/xml/xmlquery/argument/SortArgument.class.php b/classes/xml/xmlquery/argument/SortArgument.class.php deleted file mode 100644 index a89c56bb5..000000000 --- a/classes/xml/xmlquery/argument/SortArgument.class.php +++ /dev/null @@ -1,20 +0,0 @@ - */ - -/** - * SortArgument class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/argument - * @version 0.1 - */ -class SortArgument extends Argument -{ - - function getValue() - { - return $this->getUnescapedValue(); - } - -} -/* End of file SortArgument.class.php */ -/* Location: ./classes/xml/xmlquery/argument/SortArgument.class.php */ diff --git a/classes/xml/xmlquery/queryargument/DefaultValue.class.php b/classes/xml/xmlquery/queryargument/DefaultValue.class.php deleted file mode 100644 index 0e0d53471..000000000 --- a/classes/xml/xmlquery/queryargument/DefaultValue.class.php +++ /dev/null @@ -1,177 +0,0 @@ - */ - -/** - * DefaultValue class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/queryargument - * @version 0.1 - */ -class DefaultValue -{ - - /** - * Column name - * @var string - */ - var $column_name; - - /** - * Value - * @var mixed - */ - var $value; - - /** - * sequnence status - * @var bool - */ - var $is_sequence = FALSE; - - /** - * operation status - * @var bool - */ - var $is_operation = FALSE; - - /** - * operation - * @var string - */ - var $operation = ''; - - /** - * Checks if value is plain string or name of XE function (ipaddress, plus, etc). - * @var bool - */ - var $_is_string = FALSE; - - /** - * Checks if value is string resulted from evaluating a piece of PHP code (see $_SERVER[REMOTE_ADDR]) - * @var bool - */ - var $_is_string_from_function = FALSE; - - /** - * constructor - * @param string $column_name column name - * @param mixed $value value - * @return void - */ - function __construct($column_name, $value) - { - $dbParser = DB::getParser(); - $this->column_name = $dbParser->parseColumnName($column_name); - $this->value = $value; - $this->value = $this->_setValue(); - } - - function isString() - { - return $this->_is_string; - $str_pos = strpos($this->value, '('); - if($str_pos === false) - { - return TRUE; - } - return FALSE; - } - - function isStringFromFunction() - { - return $this->_is_string_from_function; - } - - function isSequence() - { - return $this->is_sequence; - } - - function isOperation() - { - return $this->is_operation; - } - - function getOperation() - { - return $this->operation; - } - - function _setValue() - { - if(!isset($this->value)) - { - return; - } - - // If value contains comma separated values and does not contain paranthesis - // -> default value is an array - if(strpos($this->value, ',') !== FALSE && strpos($this->value, '(') === FALSE) - { - return sprintf('array(%s)', $this->value); - } - - $str_pos = strpos($this->value, '('); - // // TODO Replace this with parseExpression - if($str_pos === FALSE) - { - $this->_is_string = TRUE; - return '\'' . $this->value . '\''; - } - //if($str_pos===false) return $this->value; - - $func_name = substr($this->value, 0, $str_pos); - $args = substr($this->value, $str_pos + 1, -1); - - switch($func_name) - { - case 'ipaddress' : - $val = '\\RX_CLIENT_IP'; - $this->_is_string_from_function = TRUE; - break; - case 'unixtime' : - $val = '\\RX_TIME'; - $this->_is_string_from_function = TRUE; - break; - case 'curdate' : - $val = 'getInternalDateTime()'; - $this->_is_string_from_function = TRUE; - break; - case 'sequence' : - $this->is_sequence = TRUE; - $val = '$sequence'; - break; - case 'plus' : - $args = abs($args); - $this->is_operation = TRUE; - $this->operation = '+'; - $val = sprintf('%d', $args); - break; - case 'minus' : - $args = abs($args); - $this->is_operation = TRUE; - $this->operation = '-'; - $val = sprintf('%d', $args); - break; - case 'multiply' : - $args = intval($args); - $this->is_operation = TRUE; - $this->operation = '*'; - $val = sprintf('%d', $args); - break; - default : - $val = '\'' . $this->value . '\''; - //$val = $this->value; - } - - return $val; - } - - function toString() - { - return $this->value; - } - -} -/* End of file DefaultValue.class.php */ -/* Location: ./classes/xml/xmlquery/queryargument/DefaultValue.class.php */ diff --git a/classes/xml/xmlquery/queryargument/QueryArgument.class.php b/classes/xml/xmlquery/queryargument/QueryArgument.class.php deleted file mode 100644 index 7e97975b6..000000000 --- a/classes/xml/xmlquery/queryargument/QueryArgument.class.php +++ /dev/null @@ -1,191 +0,0 @@ - */ - -/** - * QueryArgument class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/queryargument - * @version 0.1 - */ -class QueryArgument -{ - - /** - * Argument name - * @var string - */ - var $argument_name; - - /** - * Variable name - * @var string - */ - var $variable_name; - - /** - * Argument validator - * @var QueryArgumentValidator - */ - var $argument_validator; - - /** - * Column name - * @var string - */ - var $column_name; - - /** - * Table name - * @var string - */ - var $table_name; - - /** - * Operation - * @var string - */ - var $operation; - - /** - * Ignore value - * @var bool - */ - var $ignore_value; - - /** - * constructor - * @param object $tag tag object - * @param bool $ignore_value - * @return void - */ - function __construct($tag, $ignore_value = FALSE) - { - static $number_of_arguments = 0; - - $this->argument_name = $tag->attrs->var; - if(!$this->argument_name) - { - $this->argument_name = str_replace('.', '_', $tag->attrs->name); - } - if(!$this->argument_name) - { - $this->argument_name = str_replace('.', '_', $tag->attrs->column); - } - - $this->variable_name = $this->argument_name; - - $number_of_arguments++; - $this->argument_name .= $number_of_arguments; - - $name = $tag->attrs->name; - if(!$name) - { - $name = $tag->attrs->column; - } - if(strpos($name, '.') === FALSE) - { - $this->column_name = $name; - } - else - { - list($prefix, $name) = explode('.', $name); - $this->column_name = $name; - $this->table_name = $prefix; - } - - if($tag->attrs->operation) - { - $this->operation = $tag->attrs->operation; - } - - $this->argument_validator = new QueryArgumentValidator($tag, $this); - $this->ignore_value = $ignore_value; - } - - function getArgumentName() - { - return $this->argument_name; - } - - function getColumnName() - { - return $this->column_name; - } - - function getTableName() - { - return $this->table_name; - } - - function getValidatorString() - { - return $this->argument_validator->toString(); - } - - function isConditionArgument() - { - if($this->operation) - { - return TRUE; - } - return FALSE; - } - - /** - * Change QueryArgument object to string - * @return string - */ - function toString() - { - if($this->isConditionArgument()) - { - // Instantiation - $arg = sprintf("\n" . '${\'%s_argument\'} = new ConditionArgument(\'%s\', %s, \'%s\');' . "\n" - , $this->argument_name - , $this->variable_name - , '$args->' . $this->variable_name - , $this->operation - ); - // Call methods to validate argument and ensure default value - $arg .= $this->argument_validator->toString(); - - // Prepare condition string - $arg .= sprintf('${\'%s_argument\'}->createConditionValue();' . "\n" - , $this->argument_name - ); - - // Check that argument passed validation, else return - $arg .= sprintf('if(!${\'%s_argument\'}->isValid()) return ${\'%s_argument\'}->getErrorMessage();' . "\n" - , $this->argument_name - , $this->argument_name - ); - } - else - { - $arg = sprintf("\n" . '${\'%s_argument\'} = new Argument(\'%s\', %s);' . "\n" - , $this->argument_name - , $this->variable_name - , $this->ignore_value ? 'NULL' : '$args->{\'' . $this->variable_name . '\'}'); - - $arg .= $this->argument_validator->toString(); - - $arg .= sprintf('if(!${\'%s_argument\'}->isValid()) return ${\'%s_argument\'}->getErrorMessage();' . "\n" - , $this->argument_name - , $this->argument_name - ); - } - - // If the argument is null, skip it - if($this->argument_validator->isIgnorable()) - { - $arg = sprintf("if(isset(%s)) {", '$args->' . $this->variable_name) - . $arg - . sprintf("} else\n" . '${\'%s_argument\'} = NULL;', $this->argument_name); - } - - return $arg; - } - -} -/* End of file QueryArgument.class.php */ -/* Location: ./classes/xml/xmlquery/queryargument/QueryArgument.class.php */ diff --git a/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php b/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php deleted file mode 100644 index f757c3b9c..000000000 --- a/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php +++ /dev/null @@ -1,34 +0,0 @@ - */ - -/** - * SortQueryArgument class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/queryargument - * @version 0.1 - */ -class SortQueryArgument extends QueryArgument -{ - - /** - * Change SortQueryArgument object to string - * @return string - */ - function toString() - { - $arg = sprintf("\n" . '${\'%s_argument\'} = new SortArgument(\'%s\', %s);' . "\n" - , $this->argument_name - , $this->argument_name - , '$args->' . $this->variable_name); - $arg .= $this->argument_validator->toString(); - - $arg .= sprintf('if(!${\'%s_argument\'}->isValid()) return ${\'%s_argument\'}->getErrorMessage();' . "\n" - , $this->argument_name - , $this->argument_name - ); - return $arg; - } - -} -/* End of file DefaultValue.class.php */ -/* Location: ./classes/xml/xmlquery/queryargument/DefaultValue.class.php */ diff --git a/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php b/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php deleted file mode 100644 index 2bcfbe1b9..000000000 --- a/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php +++ /dev/null @@ -1,135 +0,0 @@ - */ - -/** - * QueryArgumentValidator class - * @author NAVER (developers@xpressengine.com) - * @package /classes/xml/xmlquery/queryargument/validator - * @version 0.1 - */ -class QueryArgumentValidator -{ - - /** - * Argument name - * @var string - */ - var $argument_name; - - /** - * Default value - * @var string - */ - var $default_value; - - /** - * Notnull status setting, if value should be not null, this value is 'notnull' - * @var string - */ - var $notnull; - - /** - * Filter for value type, for example number - * @var string - */ - var $filter; - - /** - * Minimum length for value - * @var int - */ - var $min_length; - - /** - * Maximum length for value - * @var int - */ - var $max_length; - var $validator_string; - - /** - * Query argument for validate - * @var QueryArgument object - */ - var $argument; - - /** - * constructor - * @param Xml_Node_ $tag tag object by Query xml file parse - * @param QueryArgument $argument - * @return void - */ - function __construct($tag, $argument) - { - $this->argument = $argument; - $this->argument_name = $this->argument->getArgumentName(); - - $this->default_value = $tag->attrs->default; - $this->notnull = $tag->attrs->notnull; - $this->filter = $tag->attrs->filter; - $this->min_length = $tag->attrs->min_length; - $this->max_length = $tag->attrs->max_length; - } - - function isIgnorable() - { - if(isset($this->default_value) || isset($this->notnull)) - { - return FALSE; - } - return TRUE; - } - - function toString() - { - $validator = ''; - if($this->filter) - { - $validator .= sprintf('${\'%s_argument\'}->checkFilter(\'%s\');' . "\n" - , $this->argument_name - , $this->filter - ); - } - if($this->min_length) - { - $validator .= sprintf('${\'%s_argument\'}->checkMinLength(%s);' . "\n" - , $this->argument_name - , $this->min_length - ); - } - if($this->max_length) - { - $validator .= sprintf('${\'%s_argument\'}->checkMaxLength(%s);' . "\n" - , $this->argument_name - , $this->max_length - ); - } - if(isset($this->default_value)) - { - $this->default_value = new DefaultValue($this->argument_name, $this->default_value); - if($this->default_value->isSequence()) - $validator .= '$db = DB::getInstance(); $sequence = $db->getNextSequence(); '; - if($this->default_value->isOperation()) - { - $validator .= sprintf('${\'%s_argument\'}->setColumnOperation(\'%s\');' . "\n" - , $this->argument_name - , $this->default_value->getOperation() - ); - } - $validator .= sprintf('${\'%s_argument\'}->ensureDefaultValue(%s);' . "\n" - , $this->argument_name - , $this->default_value->toString() - ); - } - if($this->notnull) - { - $validator .= sprintf('${\'%s_argument\'}->checkNotNull();' . "\n" - , $this->argument_name - ); - } - return $validator; - } - -} -/* End of file QueryArgumentValidator.class.php */ -/* Location: ./classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php */ diff --git a/classes/xml/xmlquery/tags/column/ColumnTag.class.php b/classes/xml/xmlquery/tags/column/ColumnTag.class.php deleted file mode 100644 index 4ed4ae34d..000000000 --- a/classes/xml/xmlquery/tags/column/ColumnTag.class.php +++ /dev/null @@ -1,35 +0,0 @@ - */ - -/** - * Models the <column> tag inside an XML Query file
- * Since the <column> tag supports different attributes depending on - * the type of query (select, update, insert, delete) this is only - * the base class for the classes that will model each type tag. - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class ColumnTag -{ - - /** - * Column name - * @var string - */ - var $name; - - /** - * Constructor - * @param string $name - * @return void - */ - function __construct($name) - { - $this->name = $name; - } - -} -/* End of file ColumnTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/ColumnTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php b/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php deleted file mode 100644 index d8c412b8d..000000000 --- a/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php +++ /dev/null @@ -1,62 +0,0 @@ - */ - -/** - * Models the <column> tag inside an XML Query file whose action is 'insert' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class InsertColumnTag extends ColumnTag -{ - - /** - * Argument - * - * @var QueryArgument object - */ - var $argument; - - /** - * Constructor - * - * @param object $column - * - * @return void - */ - function __construct($column) - { - parent::__construct($column->attrs->name); - $dbParser = DB::getParser(); - $this->name = $dbParser->parseColumnName($this->name); - $this->argument = new QueryArgument($column); - } - - /** - * Returns the string to be output in the cache file - * used for instantiating an InsertExpression when a - * query is executed - * - * @return string - */ - function getExpressionString() - { - return sprintf('new InsertExpression(\'%s\', ${\'%s_argument\'})' - , $this->name - , $this->argument->argument_name); - } - - /** - * Returns the QueryArgument object associated with this INSERT statement - * - * @return QueryArgument - */ - function getArgument() - { - return $this->argument; - } - -} -/* End of file InsertColumnTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/InsertColumnTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php b/classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php deleted file mode 100644 index f5cf226d4..000000000 --- a/classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php +++ /dev/null @@ -1,49 +0,0 @@ - */ - -/** - * Models the <column> tag inside an XML Query file whose action is 'insert-select' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class InsertColumnTagWithoutArgument extends ColumnTag -{ - - /** - * Constructor - * - * @param object $column - * @return void - */ - function __construct($column) - { - parent::__construct($column->attrs->name); - $dbParser = DB::getParser(); - $this->name = $dbParser->parseColumnName($this->name); - } - - /** - * Returns the string to be output in the cache file - * - * @return string - */ - function getExpressionString() - { - return sprintf('new Expression(\'%s\')', $this->name); - } - - /** - * Returns the QueryArgument object associated with this INSERT statement - * - * @return null - */ - function getArgument() - { - return NULL; - } - -} -/* End of file InsertColumnTagWithoutArgument.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php */ diff --git a/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php b/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php deleted file mode 100644 index 91c2f997e..000000000 --- a/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php +++ /dev/null @@ -1,92 +0,0 @@ - */ - -/** - * Models the <columns> tag inside an XML Query file whose action is 'insert' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class InsertColumnsTag -{ - - /** - * Column list - * - * @var array value is InsertColumnTag object - */ - var $columns; - - /** - * Constructor - * - * @param array|string $xml_columns - * @return void - */ - function __construct($xml_columns) - { - $this->columns = array(); - - if(!$xml_columns) - { - return; - } - - if(!is_array($xml_columns)) - { - $xml_columns = array($xml_columns); - } - - foreach($xml_columns as $column) - { - if($column->name === 'query') - { - $this->columns[] = new QueryTag($column, TRUE); - } - else if(!isset($column->attrs->var) && !isset($column->attrs->default)) - { - $this->columns[] = new InsertColumnTagWithoutArgument($column); - } - else - { - $this->columns[] = new InsertColumnTag($column); - } - } - } - - /** - * InsertColumnTag object to string - * - * @return string - */ - function toString() - { - $output_columns = 'array(' . PHP_EOL; - foreach($this->columns as $column) - { - $output_columns .= $column->getExpressionString() . PHP_EOL . ','; - } - $output_columns = substr($output_columns, 0, -1); - $output_columns .= ')'; - return $output_columns; - } - - /** - * Return argument list - * - * @return array - */ - function getArguments() - { - $arguments = array(); - foreach($this->columns as $column) - { - $arguments[] = $column->getArgument(); - } - return $arguments; - } - -} -/* End of file InsertColumnsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php b/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php deleted file mode 100644 index a955d0045..000000000 --- a/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php +++ /dev/null @@ -1,84 +0,0 @@ - */ - -/** - * Models the <column> tag inside an XML Query file whose action is 'select' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class SelectColumnTag extends ColumnTag -{ - - /** - * Column alias - * - * @var string - */ - var $alias; - - /** - * Click count status - * - * @var bool - */ - var $click_count; - - /** - * Constructor - * - * @param string|object $column - * @return void - */ - function __construct($column) - { - if($column == "*" || $column->attrs->name == '*') - { - parent::__construct(NULL); - $this->name = "*"; - } - else - { - parent::__construct($column->attrs->name); - $dbParser = DB::getParser(); - $this->name = $dbParser->parseExpression($this->name); - - $this->alias = $column->attrs->alias; - $this->click_count = $column->attrs->click_count; - } - } - - /** - * Returns the string to be output in the cache file - * - * A select column tag in an XML query can be used for: - *
    - *
  • a star expression: SELECT * - *
  • a click count expression: SELECT + UPDATE - *
  • any other select expression (column name, function call etc).
  • - *
- * - * @return string - */ - function getExpressionString() - { - if($this->name == '*') - { - return "new StarExpression()"; - } - if($this->click_count) - { - return sprintf('new ClickCountExpression(\'%s\', %s, $args->%s)', $this->name, $this->alias ? '\'' . $this->alias . '\'' : "''", $this->click_count); - } - if(strpos($this->name, '$') === 0) - { - return sprintf('new SelectExpression($args->%s)', substr($this->name, 1)); - } - $dbParser = DB::getParser(); - return sprintf('new SelectExpression(\'%s\'%s)', $this->name, $this->alias ? ', \'' . $dbParser->escape($this->alias) . '\'' : ''); - } - -} -/* End of file SelectColumnTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/SelectColumnTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php b/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php deleted file mode 100644 index 96975041c..000000000 --- a/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php +++ /dev/null @@ -1,117 +0,0 @@ - */ - -/** - * Models the <columns> tag inside an XML Query file whose action is 'select' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class SelectColumnsTag -{ - - /** - * Column list - * - * @var array value is SelectColumnTag object - */ - var $columns; - - /** - * Constructor - * - * @param $xml_columns_tag - * @internal param \Xml_Node_ $xml_columns - * @return void - */ - function __construct($xml_columns_tag) - { - if(!$xml_columns_tag) - { - $xml_columns_tag = new Xml_Node_(); - } - - $xml_columns = $xml_columns_tag->column; - $xml_queries = $xml_columns_tag->query; - - $this->columns = array(); - - if(!$xml_columns) - { - $this->columns[] = new SelectColumnTag("*"); - return; - } - - if(!is_array($xml_columns)) - { - $xml_columns = array($xml_columns); - } - - foreach($xml_columns as $column) - { - $this->columns[] = new SelectColumnTag($column); - } - - - if(!$xml_queries) - { - return; - } - - if(!is_array($xml_queries)) - { - $xml_queries = array($xml_queries); - } - - foreach($xml_queries as $column) - { - $this->columns[] = new QueryTag($column, TRUE); - } - } - - /** - * Returns the string to be output in the cache file - * - * @return string - */ - function toString() - { - $output_columns = 'array(' . PHP_EOL; - foreach($this->columns as $column) - { - if(is_a($column, 'QueryTag')) - { - $output_columns .= $column->toString() . PHP_EOL . ','; - } - else - { - $output_columns .= $column->getExpressionString() . PHP_EOL . ','; - } - } - $output_columns = substr($output_columns, 0, -1); - $output_columns .= ')'; - return $output_columns; - } - - /** - * Return argument list - * - * @return array - */ - function getArguments() - { - $arguments = array(); - foreach($this->columns as $column) - { - if(is_a($column, 'QueryTag')) - { - $arguments = array_merge($arguments, $column->getArguments()); - } - } - return $arguments; - } - -} -/* End of file SelectColumnsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php b/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php deleted file mode 100644 index 55c390e75..000000000 --- a/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php +++ /dev/null @@ -1,108 +0,0 @@ - */ - -/** - * Models the <column> tag inside an XML Query file whose action is 'update' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class UpdateColumnTag extends ColumnTag -{ - - /** - * Argument - * - * @var QueryArgument object - */ - var $argument; - - /** - * Default value - * - * @var string - */ - var $default_value; - - /** - * Constructor - * - * @param object $column - * @return void - */ - function __construct($column) - { - parent::__construct($column->attrs->name); - - $dbParser = DB::getParser(); - $this->name = $dbParser->parseColumnName($this->name); - - if($column->attrs->var) - { - $this->argument = new QueryArgument($column); - } - else - { - if(strpos($column->attrs->default, '.') !== FALSE) - { - $this->default_value = "'" . $dbParser->parseColumnName($column->attrs->default) . "'"; - } - else - { - $default_value = new DefaultValue($this->name, $column->attrs->default); - if($default_value->isOperation()) - { - $this->argument = new QueryArgument($column, TRUE); - } - //else $this->default_value = $dbParser->parseColumnName($column->attrs->default); - else - { - $this->default_value = $default_value->toString(); - if($default_value->isStringFromFunction()) - { - $this->default_value = '"\'".' . $this->default_value . '."\'"'; - } - if($default_value->isString()) - { - $this->default_value = '"' . $this->default_value . '"'; - } - } - } - } - } - - /** - * Returns the string to be output in the cache file - * - * @return string - */ - function getExpressionString() - { - if($this->argument) - { - return sprintf('new UpdateExpression(\'%s\', ${\'%s_argument\'})' - , $this->name - , $this->argument->argument_name); - } - else - { - return sprintf('new UpdateExpressionWithoutArgument(\'%s\', %s)' - , $this->name - , $this->default_value); - } - } - - /** - * Returns the Argument associated with this update statement - * - * @return QueryArgument - */ - function getArgument() - { - return $this->argument; - } - -} -/* End of file UpdateColumnTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php */ diff --git a/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php b/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php deleted file mode 100644 index be0fea125..000000000 --- a/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php +++ /dev/null @@ -1,83 +0,0 @@ - */ - -/** - * Models the <columns> tag inside an XML Query file whose action is 'update' - * - * @author Corina Udrescu (corina.udrescu@arnia.ro) - * @package classes\xml\xmlquery\tags\column - * @version 0.1 - */ -class UpdateColumnsTag -{ - - /** - * Column list - * - * @var array value is UpdateColumnTag object - */ - var $columns; - - /** - * Constructor - * - * @param array|object $xml_columns - * @return void - */ - function __construct($xml_columns) - { - $this->columns = array(); - - if(!is_array($xml_columns)) - { - $xml_columns = array($xml_columns); - } - - foreach($xml_columns as $column) - { - if($column->name === 'query') - { - $this->columns[] = new QueryTag($column, true); - } - else - { - $this->columns[] = new UpdateColumnTag($column); - } - } - } - - /** - * Returns the string to be output in the cache file - * - * @return string - */ - function toString() - { - $output_columns = 'array(' . PHP_EOL; - foreach($this->columns as $column) - { - $output_columns .= $column->getExpressionString() . PHP_EOL . ','; - } - $output_columns = substr($output_columns, 0, -1); - $output_columns .= ')'; - return $output_columns; - } - - /** - * Return argument list - * - * @return array - */ - function getArguments() - { - $arguments = array(); - foreach($this->columns as $column) - { - $arguments[] = $column->getArgument(); - } - return $arguments; - } - -} -/* End of file UpdateColumnsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php deleted file mode 100644 index 8c27d66e3..000000000 --- a/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php +++ /dev/null @@ -1,99 +0,0 @@ - */ - -/** - * ConditionGroupTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/condition - * @version 0.1 - */ -class ConditionGroupTag -{ - - /** - * condition list - * @var string|array value is ConditionTag object - */ - var $conditions; - - /** - * pipe - * @var string - */ - var $pipe; - - /** - * constructor - * @param string|array $conditions - * @param string $pipe - * @return void - */ - function __construct($conditions, $pipe = 'and') - { - $this->pipe = $pipe; - - if(!is_array($conditions)) - { - $conditions = array($conditions); - } - - foreach($conditions as $condition) - { - if($condition->node_name === 'group') - { - $subconditions = $condition->condition; - $subgroups = $condition->group; - $subconditions = $subconditions ? (is_array($subconditions) ? $subconditions : [$subconditions]) : []; - $subgroups = $subgroups ? (is_array($subgroups) ? $subgroups : [$subgroups]) : []; - $this->conditions[] = new ConditionGroupTag(array_merge($subconditions, $subgroups), $condition->attrs->pipe); - } - else - { - $this->conditions[] = new ConditionTag($condition); - } - } - } - - function getConditions() - { - return $this->conditions; - } - - /** - * ConditionTag object to string - * @return string - */ - function getConditionGroupString() - { - $conditions_string = 'array(' . PHP_EOL; - foreach($this->conditions as $condition) - { - if($condition instanceof ConditionGroupTag) - { - $conditions_string .= $condition->getConditionGroupString() . PHP_EOL . ','; - } - else - { - $conditions_string .= $condition->getConditionString() . PHP_EOL . ','; - } - } - $conditions_string = substr($conditions_string, 0, -2); //remove ',' - $conditions_string .= ')'; - - return sprintf("new ConditionGroup(%s%s)", $conditions_string, $this->pipe ? ',\'' . $this->pipe . '\'' : ''); - } - - function getArguments() - { - $arguments = array(); - foreach($this->conditions as $condition) - { - $arguments = array_merge($arguments, $condition->getArguments()); - } - return $arguments; - } - -} -/* End of file ConditionGroupTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php */ diff --git a/classes/xml/xmlquery/tags/condition/ConditionTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionTag.class.php deleted file mode 100644 index fe4ce7ee1..000000000 --- a/classes/xml/xmlquery/tags/condition/ConditionTag.class.php +++ /dev/null @@ -1,187 +0,0 @@ - */ - -/** - * ConditionTag - * Models the tag inside an XML Query file. Base class. - * - * @author Corina - * @package /classes/xml/xmlquery/tags/condition - * @version 0.1 - */ -class ConditionTag -{ - - /** - * operation for example 'in', 'between', 'not in'... - * @var string - */ - var $operation; - - /** - * Column name - * @var string - */ - var $column_name; - - /** - * Pipe - * @var string - */ - var $pipe; - - /** - * Argument name - * @var string - */ - var $argument_name; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $argument; - - /** - * Default column - * @var string - */ - var $default_column; - - /** - * QueryTag object - * @var QueryTag - */ - var $query; - - /** - * constructor - * @param object $condition - * @return void - */ - function __construct($condition) - { - $this->operation = $condition->attrs->operation; - $this->pipe = $condition->attrs->pipe ?: 'and'; - $dbParser = DB::getParser(); - $this->column_name = $dbParser->parseExpression($condition->attrs->column); - - // If default value is column name, it should be escaped - if($isColumnName = (strpos($condition->attrs->default, '.') !== FALSE - && strpos($condition->attrs->default, '.') !== 0 - && strpos($condition->attrs->default, '%') === FALSE )) - { - $condition->attrs->default = $dbParser->parseExpression($condition->attrs->default); - } - - if($condition->node_name == 'query') - { - $this->query = new QueryTag($condition, TRUE); - $this->default_column = $this->query->toString(); - } - else if($condition->attrs->var && !strpos($condition->attrs->var, '.')) - { - $this->argument = new QueryArgument($condition); - $this->argument_name = $this->argument->getArgumentName(); - } - else - { - if(isset($condition->attrs->default)) - { - $operationList = array('in' => 1, 'between' => 1, 'notin' => 1, 'not_in' => 1); - if(isset($operationList[$this->operation])) - { - $default_value = $condition->attrs->default; - if(strpos($default_value, "'") !== FALSE) - { - $default_value = "\"" . $default_value . "\""; - } - else - { - $default_value = "'" . $default_value . "'"; - } - } - else - { - $default_value_object = new DefaultValue($this->column_name, $condition->attrs->default); - $default_value = $default_value_object->toString(); - - if($default_value_object->isStringFromFunction()) - { - $default_value = '"\'".' . $default_value . '."\'"'; - } - - if($default_value_object->isString() && !$isColumnName && !is_numeric($condition->attrs->default)) - { - if(strpos($default_value, "'") !== FALSE) - { - $default_value = "\"" . $default_value . "\""; - } - else - { - $default_value = "'" . $default_value . "'"; - } - } - } - $this->default_column = $default_value; - } - else - { - $this->default_column = "'" . $dbParser->parseColumnName($condition->attrs->var) . "'"; - } - } - } - - function setPipe($pipe) - { - $this->pipe = $pipe; - } - - function getArguments() - { - $arguments = array(); - if($this->query) - { - $arguments = array_merge($arguments, $this->query->getArguments()); - } - if($this->argument) - { - $arguments[] = $this->argument; - } - return $arguments; - } - - function getConditionString() - { - if($this->query) - { - return sprintf("new ConditionSubquery('%s',%s,%s%s)" - , $this->column_name - , $this->default_column - , '"' . $this->operation . '"' - , $this->pipe ? ", '" . $this->pipe . "'" : '' - ); - } - else if(isset($this->default_column)) - { - return sprintf("new ConditionWithoutArgument('%s',%s,%s%s)" - , $this->column_name - , $this->default_column - , '"' . $this->operation . '"' - , $this->pipe ? ", '" . $this->pipe . "'" : '' - ); - } - else - { - return sprintf("new ConditionWithArgument('%s',%s,%s%s)" - , $this->column_name - , '$' . $this->argument_name . '_argument' - , '"' . $this->operation . '"' - , $this->pipe ? ", '" . $this->pipe . "'" : '' - ); - } - } - -} -/* End of file ConditionTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/condition/ConditionTag.class.php */ diff --git a/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php deleted file mode 100644 index 66a172297..000000000 --- a/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php +++ /dev/null @@ -1,102 +0,0 @@ - */ - -/** - * ConditionsTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/condition - * @version 0.1 - */ -class ConditionsTag -{ - - /** - * ConditionGroupTag list - * @var array value is ConditionGroupTag object - */ - var $condition_groups; - - /** - * constructor - * @param object $xml_conditions - * @return void - */ - function __construct($xml_conditions) - { - $this->condition_groups = array(); - if(!$xml_conditions) - { - return; - } - - $xml_condition_list = array(); - if($xml_conditions->condition) - { - $xml_condition_list = $xml_conditions->condition; - } - - if($xml_conditions->query) - { - if(!is_array($xml_condition_list)) - { - $xml_condition_list = array($xml_condition_list); - } - if(!is_array($xml_conditions->query)) - { - $xml_conditions->query = array($xml_conditions->query); - } - $xml_condition_list = array_merge($xml_condition_list, $xml_conditions->query); - } - if($xml_condition_list) - { - $this->condition_groups[] = new ConditionGroupTag($xml_condition_list); - } - - $xml_groups = $xml_conditions->group; - if($xml_groups) - { - if(!is_array($xml_groups)) - { - $xml_groups = array($xml_groups); - } - foreach($xml_groups as $group) - { - $subconditions = $group->condition; - $subgroups = $group->group; - $subconditions = $subconditions ? (is_array($subconditions) ? $subconditions : [$subconditions]) : []; - $subgroups = $subgroups ? (is_array($subgroups) ? $subgroups : [$subgroups]) : []; - $this->condition_groups[] = new ConditionGroupTag(array_merge($subconditions, $subgroups), $group->attrs->pipe); - } - } - } - - /** - * ConditionGroupTag object to string - * @return string - */ - function toString() - { - $output_conditions = 'array(' . PHP_EOL; - foreach($this->condition_groups as $condition) - { - $output_conditions .= $condition->getConditionGroupString() . PHP_EOL . ','; - } - $output_conditions = substr($output_conditions, 0, -1); - $output_conditions .= ')'; - return $output_conditions; - } - - function getArguments() - { - $arguments = array(); - foreach($this->condition_groups as $condition) - { - $arguments = array_merge($arguments, $condition->getArguments()); - } - return $arguments; - } - -} -/* End of file ConditionsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/condition/ConditionsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php b/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php deleted file mode 100644 index db6c0c353..000000000 --- a/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php +++ /dev/null @@ -1,27 +0,0 @@ - */ - -/** - * JoinConditionsTag class - * - * @author Corina - * @package /classes/xml/xmlquery/tags/condition - * @version 0.1 - */ -class JoinConditionsTag extends ConditionsTag -{ - - /** - * constructor - * @param object $xml_conditions - * @return void - */ - function __construct($xml_conditions) - { - parent::__construct($xml_conditions); - $this->condition_groups[0]->conditions[0]->setPipe(""); - } - -} -/* End of file JoinConditionsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/group/GroupsTag.class.php b/classes/xml/xmlquery/tags/group/GroupsTag.class.php deleted file mode 100644 index ab5e70111..000000000 --- a/classes/xml/xmlquery/tags/group/GroupsTag.class.php +++ /dev/null @@ -1,66 +0,0 @@ - */ - -/** - * GroupsTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/group - * @version 0.1 - */ -class GroupsTag -{ - - /** - * column list - * @var array - */ - var $groups; - - /** - * constructor - * @param array|string $xml_groups - * @return void - */ - function __construct($xml_groups) - { - $this->groups = array(); - - if($xml_groups) - { - if(!is_array($xml_groups)) - { - $xml_groups = array($xml_groups); - } - - $dbParser = &DB::getParser(); - for($i = 0; $i < count($xml_groups); $i++) - { - $group = $xml_groups[$i]; - $column = trim($group->attrs->column); - if(!$column) - { - continue; - } - - $column = $dbParser->parseExpression($column); - $this->groups[] = $column; - } - } - } - - function toString() - { - $output = 'array(' . PHP_EOL; - foreach($this->groups as $group) - { - $output .= "'" . $group . "' ,"; - } - $output = substr($output, 0, -1); - $output .= ')'; - return $output; - } - -} -/* End of file GroupsTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/group/GroupsTag.class.php */ diff --git a/classes/xml/xmlquery/tags/navigation/IndexTag.class.php b/classes/xml/xmlquery/tags/navigation/IndexTag.class.php deleted file mode 100644 index fdd0bb39f..000000000 --- a/classes/xml/xmlquery/tags/navigation/IndexTag.class.php +++ /dev/null @@ -1,95 +0,0 @@ - */ - -/** - * IndexTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/navigation - * @version 0.1 - */ -class IndexTag -{ - - /** - * argument name - * @var string - */ - var $argument_name; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $argument; - - /** - * Default value - * @var string - */ - var $default; - - /** - * Sort order - * @var string - */ - var $sort_order; - - /** - * Sort order argument - * @var SortQueryArgument object - */ - var $sort_order_argument; - - /** - * constructor - * @param object $index - * @return void - */ - function __construct($index) - { - $this->argument_name = $index->attrs->var; - - // Sort index - column by which to sort - //$dbParser = new DB(); $dbParser = &$dbParser->getParser(); - //$index->attrs->default = $dbParser->parseExpression($index->attrs->default); - $this->default = $index->attrs->default; - $this->argument = new QueryArgument($index); - - // Sort order - asc / desc - $this->sort_order = $index->attrs->order; - $sortList = array('asc' => 1, 'desc' => 1); - if(!isset($sortList[$this->sort_order])) - { - $arg = new Xml_Node_(); - $arg->attrs = new Xml_Node_(); - $arg->attrs->var = $this->sort_order; - $arg->attrs->default = 'asc'; - $this->sort_order_argument = new SortQueryArgument($arg); - $this->sort_order = '$' . $this->sort_order_argument->getArgumentName() . '_argument'; - } - else - { - $this->sort_order = '"' . $this->sort_order . '"'; - } - } - - function toString() - { - return sprintf('new OrderByColumn(${\'%s_argument\'}, %s)', $this->argument->getArgumentName(), $this->sort_order); - } - - function getArguments() - { - $arguments = array(); - $arguments[] = $this->argument; - if($this->sort_order_argument) - { - $arguments[] = $this->sort_order_argument; - } - return $arguments; - } - -} -/* End of file IndexTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/navigation/IndexTag.class.php */ diff --git a/classes/xml/xmlquery/tags/navigation/LimitTag.class.php b/classes/xml/xmlquery/tags/navigation/LimitTag.class.php deleted file mode 100644 index 6bef36a33..000000000 --- a/classes/xml/xmlquery/tags/navigation/LimitTag.class.php +++ /dev/null @@ -1,98 +0,0 @@ - */ - -/** - * LimitTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/navigation - * @version 0.1 - */ -class LimitTag -{ - - /** - * Value is relate to limit query - * @var array - */ - var $arguments; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $page; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $page_count; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $list_count; - - /** - * QueryArgument object - * @var QueryArgument - */ - var $offset; - - /** - * constructor - * @param object $index - * @return void - */ - function __construct($index) - { - if($index->page && $index->page->attrs && $index->page_count && $index->page_count->attrs) - { - if(!isset($index->page->attrs->default)) - $index->page->attrs->default = 1; - if(!isset($index->page_count->attrs->default)) - $index->page_count->attrs->default = 10; - $this->page = new QueryArgument($index->page); - $this->page_count = new QueryArgument($index->page_count); - $this->arguments[] = $this->page; - $this->arguments[] = $this->page_count; - } - - if(!isset($index->list_count->attrs->default)) - $index->list_count->attrs->default = 0; - $this->list_count = new QueryArgument($index->list_count); - $this->arguments[] = $this->list_count; - - if(isset($index->offset) && isset($index->offset->attrs)) - { - $this->offset = new QueryArgument($index->offset); - $this->arguments[] = $this->offset; - } - } - - function toString() - { - if($this->page) - { - return sprintf('new Limit(${\'%s_argument\'}, ${\'%s_argument\'}, ${\'%s_argument\'})', $this->list_count->getArgumentName(), $this->page->getArgumentName(), $this->page_count->getArgumentName()); - } - elseif($this->offset) - { - return sprintf('new Limit(${\'%s_argument\'}, NULL, NULL, ${\'%s_argument\'})', $this->list_count->getArgumentName(), $this->offset->getArgumentName()); - } - else - { - return sprintf('new Limit(${\'%s_argument\'})', $this->list_count->getArgumentName()); - } - } - - function getArguments() - { - return $this->arguments; - } - -} -/* End of file LimitTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/navigation/LimitTag.class.php */ diff --git a/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php b/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php deleted file mode 100644 index 50034c984..000000000 --- a/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php +++ /dev/null @@ -1,137 +0,0 @@ - */ - -/** - * NavigationTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/navigation - * @version 0.1 - */ -class NavigationTag -{ - - /** - * Order - * @var array - */ - var $order; - - /** - * List count - * @var int - */ - var $list_count; - - /** - * Page count - * @var int - */ - var $page_count; - - /** - * Page - * @var int - */ - var $page; - - /** - * Limit - * @var LimitTag object - */ - var $limit; - - /** - * constructor - * @param object $xml_navigation - * @return void - */ - function __construct($xml_navigation) - { - $this->order = array(); - if($xml_navigation) - { - $order = $xml_navigation->index; - if($order) - { - if(!is_array($order)) - { - $order = array($order); - } - foreach($order as $order_info) - { - $this->order[] = new IndexTag($order_info); - } - - if($xml_navigation->page && $xml_navigation->page->attrs || $xml_navigation->list_count && $xml_navigation->list_count->attrs) - { - $this->limit = new LimitTag($xml_navigation); - } - - if($xml_navigation->list_count) - { - $this->list_count = $xml_navigation->list_count->attrs; - } - - if($xml_navigation->page_count) - { - $this->page_count = $xml_navigation->page_count->attrs; - } - - if($xml_navigation->page) - { - $this->page = $xml_navigation->page->attrs; - } - } - } - } - - /** - * NavigationTag object to string - * @return string - */ - function getOrderByString() - { - $output = 'array(' . PHP_EOL; - foreach($this->order as $order) - { - $output .= $order->toString() . PHP_EOL . ','; - } - $output = substr($output, 0, -1); - $output .= ')'; - return $output; - } - - /** - * LimitTag object to string - * @return string - */ - function getLimitString() - { - if($this->limit) - { - return $this->limit->toString(); - } - else - { - return ""; - } - } - - function getArguments() - { - $arguments = array(); - foreach($this->order as $order) - { - $arguments = array_merge($order->getArguments(), $arguments); - } - if($this->limit) - { - $arguments = array_merge($this->limit->getArguments(), $arguments); - } - return $arguments; - } - -} -/* End of file NavigationTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/navigation/NavigationTag.class.php */ diff --git a/classes/xml/xmlquery/tags/query/QueryTag.class.php b/classes/xml/xmlquery/tags/query/QueryTag.class.php deleted file mode 100644 index 5171a8734..000000000 --- a/classes/xml/xmlquery/tags/query/QueryTag.class.php +++ /dev/null @@ -1,407 +0,0 @@ - */ - -/** - * QueryTag class - * - * @author Arnia Software - * @package /classes/xml/xmlquery/tags/query - * @version 0.1 - */ -class QueryTag -{ - - /** - * Action for example, 'select', 'insert', 'delete'... - * @var string - */ - var $action; - - /** - * Query id - * @var string - */ - var $query_id; - - /** - * Priority - * @var string - */ - var $priority; - - /** - * column type list - * @var array - */ - var $column_type; - - /** - * Query stdClass object - * @var object - */ - var $query; - - /** - * Columns in xml tags - * @var object - */ - var $columns; - - /** - * Tables in xml tags - * @var object - */ - var $tables; - - /** - * Subquery in xml tags - * @var object - */ - var $subquery; - - /** - * Conditions in xml tags - * @var object - */ - var $conditions; - - /** - * Groups in xml tags - * @var object - */ - var $groups; - - /** - * Having in xml tags - * @var object - */ - var $having; - - /** - * Navigation in xml tags - * @var object - */ - var $navigation; - - /** - * Arguments in xml tags - * @var object - */ - var $arguments; - - /** - * PreBuff - * @var string - */ - var $preBuff; - - /** - * Buff - * @var string - */ - var $buff; - - /** - * Subquery status - * @var bool - */ - var $isSubQuery; - - /** - * Join type - * @var string - */ - var $join_type; - - /** - * alias - * @var string - */ - var $alias; - - /** - * constructor - * @param object $query - * @param bool $isSubQuery - * @return void - */ - function __construct($query, $isSubQuery = FALSE) - { - $this->action = $query->attrs->action; - $this->query_id = $query->attrs->id; - $this->priority = $query->attrs->priority; - $this->query = $query; - $this->isSubQuery = $isSubQuery; - if($this->isSubQuery) - { - $this->action = 'select'; - } - if($query->attrs->alias) - { - $dbParser = DB::getParser(); - $this->alias = $dbParser->escape($query->attrs->alias); - } - $this->join_type = $query->attrs->join_type; - - $this->getColumns(); - $tables = $this->getTables(); - $this->setTableColumnTypes($tables); - $this->getSubquery(); // Used for insert-select - $this->getConditions(); - $this->getGroups(); - $this->getNavigation(); - - $this->getPrebuff(); - $this->getBuff(); - } - - function show() - { - return TRUE; - } - - function getQueryId() - { - return $this->query->attrs->query_id ? $this->query->attrs->query_id : $this->query->attrs->id; - } - - function getPriority() - { - return $this->query->attrs->priority; - } - - function getAction() - { - return $this->query->attrs->action; - } - - function setTableColumnTypes($tables) - { - $query_id = $this->getQueryId(); - if(!isset($this->column_type[$query_id])) - { - $table_tags = $tables->getTables(); - $column_type = array(); - foreach($table_tags as $table_tag) - { - if(is_a($table_tag, 'TableTag')) - { - $table_name = $table_tag->getTableName(); - $table_alias = $table_tag->getTableAlias(); - $tag_column_type = QueryParser::getTableInfo($query_id, $table_name); - $column_type[$table_alias] = $tag_column_type; - } - } - $this->column_type[$query_id] = $column_type; - } - } - - function getColumns() - { - if($this->action == 'select') - { - return $this->columns = new SelectColumnsTag($this->query->columns); - } - else if($this->action == 'insert' || $this->action == 'insert-select') - { - return $this->columns = new InsertColumnsTag($this->query->columns->column); - } - else if($this->action == 'update') - { - return $this->columns = new UpdateColumnsTag($this->query->columns->column); - } - else if($this->action == 'delete') - { - return $this->columns = null; - } - } - - function getPrebuff() - { - if($this->isSubQuery) - { - return; - } - // TODO Check if this work with arguments in join clause - $arguments = $this->getArguments(); - - $prebuff = ''; - foreach($arguments as $argument) - { - if(isset($argument)) - { - $arg_name = $argument->getArgumentName(); - if($arg_name) - { - unset($column_type); - $prebuff .= $argument->toString(); - - $table_alias = $argument->getTableName(); - if(isset($table_alias)) - { - if(isset($this->column_type[$this->getQueryId()][$table_alias][$argument->getColumnName()])) - { - $column_type = $this->column_type[$this->getQueryId()][$table_alias][$argument->getColumnName()]; - } - } - else - { - $current_tables = $this->column_type[$this->getQueryId()]; - $column_name = $argument->getColumnName(); - foreach($current_tables as $current_table) - { - if(isset($current_table[$column_name])) - { - $column_type = $current_table[$column_name]; - } - } - } - - if(isset($column_type)) - { - $prebuff .= sprintf('if(${\'%s_argument\'} !== null) ${\'%s_argument\'}->setColumnType(\'%s\');' . "\n" - , $arg_name - , $arg_name - , $column_type); - } - } - } - } - $prebuff .= "\n"; - - return $this->preBuff = $prebuff; - } - - function getBuff() - { - $buff = ''; - if($this->isSubQuery) - { - $buff = 'new Subquery('; - $buff .= "'" . $this->alias . '\', '; - $buff .= ($this->columns ? $this->columns->toString() : 'null' ) . ', ' . PHP_EOL; - $buff .= $this->tables->toString() . ',' . PHP_EOL; - $buff .= $this->conditions->toString() . ',' . PHP_EOL; - $buff .= $this->groups->toString() . ',' . PHP_EOL; - $buff .= $this->navigation->getOrderByString() . ',' . PHP_EOL; - $limit = $this->navigation->getLimitString(); - $buff .= $limit ? $limit : 'null' . PHP_EOL; - $buff .= $this->join_type ? "'" . $this->join_type . "'" : ''; - $buff .= ')'; - - $this->buff = $buff; - return $this->buff; - } - - $buff .= '$query = new Query();' . PHP_EOL; - $buff .= sprintf('$query->setQueryId("%s");%s', $this->query_id, "\n"); - $buff .= sprintf('$query->setAction("%s");%s', $this->action, "\n"); - $buff .= sprintf('$query->setPriority("%s");%s', $this->priority, "\n"); - $buff .= $this->preBuff; - if($this->columns) - { - $buff .= '$query->setColumns(' . $this->columns->toString() . ');' . PHP_EOL; - } - - $buff .= '$query->setTables(' . $this->tables->toString() . ');' . PHP_EOL; - if($this->action == 'insert-select') - { - $buff .= '$query->setSubquery(' . $this->subquery->toString() . ');' . PHP_EOL; - } - $buff .= '$query->setConditions(' . $this->conditions->toString() . ');' . PHP_EOL; - $buff .= '$query->setGroups(' . $this->groups->toString() . ');' . PHP_EOL; - $buff .= '$query->setHaving(' . $this->having->toString() . ');' . PHP_EOL; - $buff .= '$query->setOrder(' . $this->navigation->getOrderByString() . ');' . PHP_EOL; - $buff .= '$query->setLimit(' . $this->navigation->getLimitString() . ');' . PHP_EOL; - - $this->buff = $buff; - return $this->buff; - } - - function getTables() - { - if($this->query->index_hint && ($this->query->index_hint->attrs->for == 'ALL' || Context::getDBType() == strtolower($this->query->index_hint->attrs->for))) - { - return $this->tables = new TablesTag($this->query->tables, $this->query->index_hint); - } - else - { - return $this->tables = new TablesTag($this->query->tables); - } - } - - function getSubquery() - { - if($this->query->query) - { - $this->subquery = new QueryTag($this->query->query, true); - } - } - - function getConditions() - { - return $this->conditions = new ConditionsTag($this->query->conditions); - } - - function getGroups() - { - if($this->query->groups) - { - $this->groups = new GroupsTag($this->query->groups->group); - $this->having = new ConditionsTag($this->query->groups->having); - } - else - { - $this->groups = new GroupsTag(NULL); - $this->having = new ConditionsTag(array()); - } - - return $this->groups; - } - - function getNavigation() - { - return $this->navigation = new NavigationTag($this->query->navigation); - } - - function toString() - { - return $this->buff; - } - - function getTableString() - { - return $this->buff; - } - - function getConditionString() - { - return $this->buff; - } - - function getExpressionString() - { - return $this->buff; - } - - function getArguments() - { - $arguments = array(); - if($this->columns) - { - $arguments = array_merge($arguments, $this->columns->getArguments()); - } - if($this->action == 'insert-select') - { - $arguments = array_merge($arguments, $this->subquery->getArguments()); - } - $arguments = array_merge($arguments, $this->tables->getArguments()); - $arguments = array_merge($arguments, $this->conditions->getArguments()); - $arguments = array_merge($arguments, $this->having->getArguments()); - $arguments = array_merge($arguments, $this->navigation->getArguments()); - return $arguments; - } - -} -/* End of file QueryTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/navigation/QueryTag.class.php */ diff --git a/classes/xml/xmlquery/tags/table/HintTableTag.class.php b/classes/xml/xmlquery/tags/table/HintTableTag.class.php deleted file mode 100644 index ac9aea17f..000000000 --- a/classes/xml/xmlquery/tags/table/HintTableTag.class.php +++ /dev/null @@ -1,66 +0,0 @@ - */ - -/** - * HintTableTag - * Models the tag inside an XML Query file and the corresponding tag - * - * @author Arnia Sowftare - * @package /classes/xml/xmlquery/tags/table - * @version 0.1 - */ -class HintTableTag extends TableTag -{ - - /** - * Action for example, 'select', 'insert', 'delete'... - * @var array - */ - var $index; - - /** - * constructor - * Initialises Table Tag properties - * @param object $table XML
tag - * @param array $index - * @return void - */ - function __construct($table, $index) - { - parent::__construct($table); - $this->index = $index; - } - - function getTableString() - { - $dbParser = DB::getParser(); - $dbType = ucfirst(Context::getDBType()); - - $result = sprintf('new %sTableWithHint(\'%s\'%s, array(' - , starts_with('mysql', $dbType, false) ? 'Mysql' : $dbType - , $dbParser->escape($this->name) - , $this->alias ? ', \'' . $dbParser->escape($this->alias) . '\'' : ', null' - //, ', \'' . $dbParser->escape($this->index->name) .'\', \'' . $this->index->type .'\'' - ); - foreach($this->index as $indx) - { - $result .= "new IndexHint("; - $result .= '\'' . $dbParser->escape($indx->name) . '\', \'' . $indx->type . '\'' . ') , '; - } - $result = substr($result, 0, -2); - $result .= '))'; - return $result; - } - - function getArguments() - { - if(!isset($this->conditionsTag)) - { - return array(); - } - return $this->conditionsTag->getArguments(); - } - -} -/* End of file HintTableTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/table/HintTableTag.class.php */ diff --git a/classes/xml/xmlquery/tags/table/TableTag.class.php b/classes/xml/xmlquery/tags/table/TableTag.class.php deleted file mode 100644 index c171ccc48..000000000 --- a/classes/xml/xmlquery/tags/table/TableTag.class.php +++ /dev/null @@ -1,145 +0,0 @@ - */ - -/** - * TableTag - * Models the
tag inside an XML Query file - * @abstract - * Example - *
- *
- * Attributes - * name - name of the table - table prefix will be automatically added - * alias - table alias. If no value is specified, the table name will be set as default alias - * join_type - in case the table is part of a join clause, this specifies the type of join: left, right etc. - * - permitted values: 'left join','left outer join','right join','right outer join' - * Children - * Can have children of type - * - * @author Arnia Sowftare - * @package /classes/xml/xmlquery/tags/table - * @version 0.1 - */ -class TableTag -{ - - /** - * Unescaped name - * @var string - */ - var $unescaped_name; - - /** - * name - * @var string - */ - var $name; - - /** - * alias - * @var string - */ - var $alias; - - /** - * Join type - * @example 'left join', 'left outer join', 'right join', 'right outer join' - * @var string - */ - var $join_type; - - /** - * Condition object - * @var object - */ - var $conditions; - - /** - * JoinConditionsTag - * @var JoinConditionsTag object - */ - var $conditionsTag; - - /** - * constructor - * Initialises Table Tag properties - * @param object $table XML
tag - * @return void - */ - function __construct($table) - { - $dbParser = DB::getParser(); - - $this->unescaped_name = $table->attrs->name; - $this->name = $dbParser->parseTableName($table->attrs->name); - - $this->alias = $table->attrs->alias; - if(!$this->alias) - { - $this->alias = $table->attrs->name; - } - - $this->join_type = $table->attrs->type; - - $this->conditions = $table->conditions; - - if($this->isJoinTable()) - { - $this->conditionsTag = new JoinConditionsTag($this->conditions); - } - } - - function isJoinTable() - { - $joinList = array('left join' => 1, 'left outer join' => 1, 'right join' => 1, 'right outer join' => 1); - if(isset($joinList[$this->join_type]) && countobj($this->conditions)) - { - return true; - } - return false; - } - - function getTableAlias() - { - return $this->alias; - } - - function getTableName() - { - return $this->unescaped_name; - } - - /** - * Returns string for printing in PHP query cache file - * The string contains code for instantiation of either - * a Table or a JoinTable object - * @return string - */ - function getTableString() - { - $dbParser = DB::getParser(); - - if($this->isJoinTable()) - { - return sprintf('new JoinTable(\'%s\', \'%s\', "%s", %s)' - , $dbParser->escape($this->name) - , $dbParser->escape($this->alias) - , $this->join_type, $this->conditionsTag->toString()); - } - return sprintf('new Table(\'%s\'%s)' - , $dbParser->escape($this->name) - , $this->alias ? ', \'' . $dbParser->escape($this->alias) . '\'' : ''); - } - - function getArguments() - { - if(!isset($this->conditionsTag)) - { - return array(); - } - return $this->conditionsTag->getArguments(); - } - -} -/* End of file TableTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/table/TableTag.class.php */ diff --git a/classes/xml/xmlquery/tags/table/TablesTag.class.php b/classes/xml/xmlquery/tags/table/TablesTag.class.php deleted file mode 100644 index 86e94ea4f..000000000 --- a/classes/xml/xmlquery/tags/table/TablesTag.class.php +++ /dev/null @@ -1,122 +0,0 @@ - */ - -/** - * TablesTag class - * Models the tag inside an XML Query file - * @abstract - * Example - * - *
- * - * Attributes - * None. - * Children - * Can have children of type
or - * - * @author Arnia Sowftare - * @package /classes/xml/xmlquery/tags/table - * @version 0.1 - */ -class TablesTag -{ - - /** - * Table list - * @var array - */ - var $tables; - - /** - * constructor - * @param object $xml_tables_tag - * @param object $xml_index_hints_tag - * @return void - */ - function __construct($xml_tables_tag, $xml_index_hints_tag = NULL) - { - $this->tables = array(); - - $xml_tables = $xml_tables_tag->table; - if(!is_array($xml_tables)) - { - $xml_tables = array($xml_tables); - } - - if($xml_index_hints_tag) - { - $index_nodes = $xml_index_hints_tag->index; - if(!is_array($index_nodes)) - { - $index_nodes = array($index_nodes); - } - foreach($index_nodes as $index_node) - { - if(!isset($indexes[$index_node->attrs->table])) - { - $indexes[$index_node->attrs->table] = array(); - } - $count = count($indexes[$index_node->attrs->table]); - $indexes[$index_node->attrs->table][$count] = (object) NULL; - $indexes[$index_node->attrs->table][$count]->name = $index_node->attrs->name; - $indexes[$index_node->attrs->table][$count]->type = $index_node->attrs->type; - } - } - - foreach($xml_tables as $tag) - { - if($tag->attrs->query == 'true') - { - $this->tables[] = new QueryTag($tag, true); - } - else - { - if(isset($indexes[$tag->attrs->name]) && $indexes[$tag->attrs->name]) - { - $this->tables[] = new HintTableTag($tag, $indexes[$tag->attrs->name]); - } - else - { - $this->tables[] = new TableTag($tag); - } - } - } - } - - function getTables() - { - return $this->tables; - } - - function toString() - { - $output_tables = 'array(' . PHP_EOL; - foreach($this->tables as $table) - { - if(is_a($table, 'QueryTag')) - { - $output_tables .= $table->toString() . PHP_EOL . ','; - } - else - { - $output_tables .= $table->getTableString() . PHP_EOL . ','; - } - } - $output_tables = substr($output_tables, 0, -1); - $output_tables .= ')'; - return $output_tables; - } - - function getArguments() - { - $arguments = array(); - foreach($this->tables as $table) - { - $arguments = array_merge($arguments, $table->getArguments()); - } - return $arguments; - } - -} -/* End of file TablesTag.class.php */ -/* Location: ./classes/xml/xmlquery/tags/table/TablesTag.class.php */ diff --git a/common/autoload.php b/common/autoload.php index cbcd870ba..9ab819280 100644 --- a/common/autoload.php +++ b/common/autoload.php @@ -49,27 +49,6 @@ $GLOBALS['RX_AUTOLOAD_FILE_MAP'] = array_change_key_case(array( 'CacheHandler' => 'classes/cache/CacheHandler.class.php', 'Context' => 'classes/context/Context.class.php', 'DB' => 'classes/db/DB.class.php', - 'Query' => 'classes/db/queryparts/Query.class.php', - 'Subquery' => 'classes/db/queryparts/Subquery.class.php', - 'Condition' => 'classes/db/queryparts/condition/Condition.class.php', - 'ConditionGroup' => 'classes/db/queryparts/condition/ConditionGroup.class.php', - 'ConditionSubquery' => 'classes/db/queryparts/condition/ConditionSubquery.class.php', - 'ConditionWithArgument' => 'classes/db/queryparts/condition/ConditionWithArgument.class.php', - 'ConditionWithoutArgument' => 'classes/db/queryparts/condition/ConditionWithoutArgument.class.php', - 'ClickCountExpression' => 'classes/db/queryparts/expression/ClickCountExpression.class.php', - 'DeleteExpression' => 'classes/db/queryparts/expression/DeleteExpression.class.php', - 'Expression' => 'classes/db/queryparts/expression/Expression.class.php', - 'InsertExpression' => 'classes/db/queryparts/expression/InsertExpression.class.php', - 'SelectExpression' => 'classes/db/queryparts/expression/SelectExpression.class.php', - 'StarExpression' => 'classes/db/queryparts/expression/StarExpression.class.php', - 'UpdateExpression' => 'classes/db/queryparts/expression/UpdateExpression.class.php', - 'UpdateExpressionWithoutArgument' => 'classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php', - 'Limit' => 'classes/db/queryparts/limit/Limit.class.php', - 'OrderByColumn' => 'classes/db/queryparts/order/OrderByColumn.class.php', - 'IndexHint' => 'classes/db/queryparts/table/IndexHint.class.php', - 'JoinTable' => 'classes/db/queryparts/table/JoinTable.class.php', - 'MysqlTableWithHint' => 'classes/db/queryparts/table/MysqlTableWithHint.class.php', - 'Table' => 'classes/db/queryparts/table/Table.class.php', 'DisplayHandler' => 'classes/display/DisplayHandler.class.php', 'HTMLDisplayHandler' => 'classes/display/HTMLDisplayHandler.php', 'JSCallbackDisplayHandler' => 'classes/display/JSCallbackDisplayHandler.php', @@ -104,36 +83,6 @@ $GLOBALS['RX_AUTOLOAD_FILE_MAP'] = array_change_key_case(array( 'XmlJsFilter' => 'classes/xml/XmlJsFilter.class.php', 'XmlLangParser' => 'classes/xml/XmlLangParser.class.php', 'XmlParser' => 'classes/xml/XmlParser.class.php', - 'XmlQueryParser' => 'classes/xml/XmlQueryParser.class.php', - 'DBParser' => 'classes/xml/xmlquery/DBParser.class.php', - 'QueryParser' => 'classes/xml/xmlquery/QueryParser.class.php', - 'Argument' => 'classes/xml/xmlquery/argument/Argument.class.php', - 'ConditionArgument' => 'classes/xml/xmlquery/argument/ConditionArgument.class.php', - 'SortArgument' => 'classes/xml/xmlquery/argument/SortArgument.class.php', - 'DefaultValue' => 'classes/xml/xmlquery/queryargument/DefaultValue.class.php', - 'QueryArgument' => 'classes/xml/xmlquery/queryargument/QueryArgument.class.php', - 'SortQueryArgument' => 'classes/xml/xmlquery/queryargument/SortQueryArgument.class.php', - 'QueryArgumentValidator' => 'classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php', - 'ColumnTag' => 'classes/xml/xmlquery/tags/column/ColumnTag.class.php', - 'InsertColumnTag' => 'classes/xml/xmlquery/tags/column/InsertColumnTag.class.php', - 'InsertColumnTagWithoutArgument' => 'classes/xml/xmlquery/tags/column/InsertColumnTagWithoutArgument.class.php', - 'InsertColumnsTag' => 'classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php', - 'SelectColumnTag' => 'classes/xml/xmlquery/tags/column/SelectColumnTag.class.php', - 'SelectColumnsTag' => 'classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php', - 'UpdateColumnTag' => 'classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php', - 'UpdateColumnsTag' => 'classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php', - 'ConditionGroupTag' => 'classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php', - 'ConditionTag' => 'classes/xml/xmlquery/tags/condition/ConditionTag.class.php', - 'ConditionsTag' => 'classes/xml/xmlquery/tags/condition/ConditionsTag.class.php', - 'JoinConditionsTag' => 'classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php', - 'GroupsTag' => 'classes/xml/xmlquery/tags/group/GroupsTag.class.php', - 'IndexTag' => 'classes/xml/xmlquery/tags/navigation/IndexTag.class.php', - 'LimitTag' => 'classes/xml/xmlquery/tags/navigation/LimitTag.class.php', - 'NavigationTag' => 'classes/xml/xmlquery/tags/navigation/NavigationTag.class.php', - 'QueryTag' => 'classes/xml/xmlquery/tags/query/QueryTag.class.php', - 'HintTableTag' => 'classes/xml/xmlquery/tags/table/HintTableTag.class.php', - 'TableTag' => 'classes/xml/xmlquery/tags/table/TableTag.class.php', - 'TablesTag' => 'classes/xml/xmlquery/tags/table/TablesTag.class.php', 'Bmp' => 'common/libraries/bmp.php', 'Ftp' => 'common/libraries/ftp.php', 'Tar' => 'common/libraries/tar.php', diff --git a/common/legacy.php b/common/legacy.php index 2bb87f44b..a3363364e 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -138,9 +138,9 @@ function getClass($module_name) * @param string[] $arg_columns Column list * @return object Query result data */ -function executeQuery($query_id, $args = NULL, $arg_columns = NULL) +function executeQuery($query_id, $args = [], $arg_columns = NULL) { - $oDB = DB::getInstance(); + $oDB = Rhymix\Framework\DB::getInstance(); return $oDB->executeQuery($query_id, $args, $arg_columns); } @@ -154,9 +154,9 @@ function executeQuery($query_id, $args = NULL, $arg_columns = NULL) * @param string[] $arg_columns Column list * @return object Query result data */ -function executeQueryArray($query_id, $args = NULL, $arg_columns = NULL) +function executeQueryArray($query_id, $args = [], $arg_columns = NULL) { - $oDB = DB::getInstance(); + $oDB = Rhymix\Framework\DB::getInstance(); $output = $oDB->executeQuery($query_id, $args, $arg_columns); if(isset($output->data) && is_object($output->data)) { @@ -173,8 +173,7 @@ function executeQueryArray($query_id, $args = NULL, $arg_columns = NULL) */ function getNextSequence() { - $oDB = DB::getInstance(); - $seq = $oDB->getNextSequence(); + $seq = Rhymix\Framework\DB::getInstance()->getNextSequence(); setUserSequence($seq); return $seq; } From fa220fb1e12ff94f6db2ceea584586b0b1f386ef Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 13:22:21 +0900 Subject: [PATCH 36/47] Fix incorrect parsing of subquery in IN () clause --- .../parsers/dbquery/variablebase.php | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index 975d9fc85..a80f0dd56 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -33,7 +33,13 @@ class VariableBase $params = array(); // Process the variable or default value. - if ($this->var && Query::isValidVariable($args[$this->var])) + if ($this instanceof Query) + { + $is_expression = true; + $value = '(' . $this->getQueryString($prefix, $args) . ')'; + $params = $this->getQueryParams(); + } + elseif ($this->var && Query::isValidVariable($args[$this->var])) { $this->filterValue($args[$this->var]); $is_expression = false; @@ -43,12 +49,6 @@ class VariableBase { list($is_expression, $value) = $this->getDefaultValue(); } - elseif ($this instanceof Query) - { - $is_expression = true; - $value = '(' . $this->getQueryString($prefix, $args) . ') AS ' . Query::quoteName($this->alias); - $params = $this->getQueryParams(); - } elseif ($this->not_null) { throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $this->column . ' is not set'); @@ -63,11 +63,11 @@ class VariableBase // Prepare the target value. $list_ops = array('in' => true, 'notin' => true, 'not_in' => true, 'between' => true); - if (isset($list_ops[$this->operation]) && !is_array($value) && $value !== '') + if (isset($list_ops[$this->operation]) && !$is_expression && !is_array($value) && $value !== '') { $value = explode(',', preg_replace('/[\s\']/', '', $value)); } - + // Apply the operator. switch ($this->operation) { @@ -161,22 +161,36 @@ class VariableBase $where = sprintf('%s IS NOT NULL', $column); break; case 'in': - $count = count($value); - $placeholders = implode(', ', array_fill(0, $count, '?')); - $where = sprintf('%s IN (%s)', $column, $placeholders); - foreach ($value as $item) + if ($is_expression) { - $params[] = $item; + $where = sprintf('%s IN %s', $column, $value); + } + else + { + $count = count($value); + $placeholders = implode(', ', array_fill(0, $count, '?')); + $where = sprintf('%s IN (%s)', $column, $placeholders); + foreach ($value as $item) + { + $params[] = $item; + } } break; case 'notin': case 'not_in': - $count = count($value); - $placeholders = implode(', ', array_fill(0, $count, '?')); - $where = sprintf('%s NOT IN (%s)', $column, $placeholders); - foreach ($value as $item) + if ($is_expression) { - $params[] = $item; + $where = sprintf('%s IN %s', $column, $value); + } + else + { + $count = count($value); + $placeholders = implode(', ', array_fill(0, $count, '?')); + $where = sprintf('%s NOT IN (%s)', $column, $placeholders); + foreach ($value as $item) + { + $params[] = $item; + } } break; case 'between': From 85df9019249926b1066256a9cbcc65836e89b009 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 14:43:42 +0900 Subject: [PATCH 37/47] Misc fixes for compatibility with existing XML queries and schemas --- common/framework/db.php | 23 ++++++++++----- common/framework/debug.php | 2 +- common/framework/helpers/dbhelper.php | 11 +++++-- common/framework/helpers/dbstmthelper.php | 4 +-- common/framework/parsers/dbquery/query.php | 29 ++++++++++++++----- .../parsers/dbquery/variablebase.php | 2 +- common/framework/parsers/dbqueryparser.php | 28 +++++++++++------- 7 files changed, 68 insertions(+), 31 deletions(-) diff --git a/common/framework/db.php b/common/framework/db.php index cc02dad91..b3f3cc42d 100644 --- a/common/framework/db.php +++ b/common/framework/db.php @@ -37,8 +37,9 @@ class DB protected $_total_time = 0; /** - * Error codes. + * Current query ID and error information. */ + protected $_query_id = ''; protected $_errno = 0; protected $_errstr = ''; @@ -270,10 +271,12 @@ class DB $last_index = 0; if ($query->requiresPagination()) { + $this->_query_id = $query_id . ' (count)'; $output = $this->_executeCountQuery($query_id, $query, $args, $last_index); if (!$output->toBool()) { $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_query_id = ''; $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -284,6 +287,7 @@ class DB $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_query_id = ''; $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -296,6 +300,7 @@ class DB // Prepare and execute the main query. try { + $this->_query_id = $query_id; if (count($query_params)) { $this->_last_stmt = $this->_handle->prepare($query_string); @@ -312,6 +317,7 @@ class DB $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_query_id = ''; $this->_total_time += (microtime(true) - $start_time); return $output; } @@ -322,14 +328,17 @@ class DB } catch (Exceptions\DBError $e) { + $output = $this->setError(-1, $e->getMessage()); $output->add('_query', $query_string); $output->add('_elapsed_time', '0.00000'); $output->page_navigation = new \PageHandler(0, 0, 0); + $this->_query_id = ''; $this->_total_time += (microtime(true) - $start_time); return $output; } // Fill query information and result data in the output object. + $this->_query_id = ''; $this->_total_time += ($elapsed_time = microtime(true) - $start_time); $output->add('_query', $query_string); $output->add('_elapsed_time', sprintf('%0.5f', $elapsed_time)); @@ -476,7 +485,7 @@ class DB { $this->setError(-1, $e->getMessage()); } - Debug::addQuery($this->getQueryLog('START TRANSACTION', '', 0)); + Debug::addQuery($this->getQueryLog('START TRANSACTION', 0)); } $this->_transaction_level++; return $this->_transaction_level; @@ -500,7 +509,7 @@ class DB { $this->setError(-1, $e->getMessage()); } - Debug::addQuery($this->getQueryLog('ROLLBACK', '', 0)); + Debug::addQuery($this->getQueryLog('ROLLBACK', 0)); } $this->_transaction_level--; return $this->_transaction_level; @@ -524,7 +533,7 @@ class DB { $this->setError(-1, $e->getMessage()); } - Debug::addQuery($this->getQueryLog('COMMIT', '', 0)); + Debug::addQuery($this->getQueryLog('COMMIT', 0)); } $this->_transaction_level--; return $this->_transaction_level; @@ -986,11 +995,10 @@ class DB * Generate a query log entry. * * @param string $query - * @param string $query_id * @param float $elapsed_time * @return array */ - public function getQueryLog(string $query, string $query_id, float $elapsed_time): array + public function getQueryLog(string $query, float $elapsed_time): array { // Cache the debug status to improve performance. static $debug_enabled = null; @@ -1007,7 +1015,7 @@ class DB // Compose the basic structure of the log entry. $result = array( 'query' => $query, - 'query_id' => $query_id, + 'query_id' => $this->_query_id, 'connection' => $this->_type, 'elapsed_time' => sprintf('%0.5f', $elapsed_time), 'result' => 'success', @@ -1029,6 +1037,7 @@ class DB { $result['called_file'] = $backtrace[$no]['file']; $result['called_line'] = $backtrace[$no]['line']; + $no++; $result['called_method'] = $backtrace[$no]['class'] . $backtrace[$no]['type'] . $backtrace[$no]['function']; $result['backtrace'] = array_slice($backtrace, $no, 1); break; diff --git a/common/framework/debug.php b/common/framework/debug.php index 37b2646a2..e31f7a218 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -355,7 +355,7 @@ class Debug 'query_id' => $query['query_id'], 'query_connection' => $query['connection'], 'query_string' => $query['query'], - 'query_time' => $query['elapsed_time'], + 'query_time' => floatval($query['elapsed_time']), 'file' => $query['called_file'], 'line' => $query['called_line'], 'method' => $query['called_method'], diff --git a/common/framework/helpers/dbhelper.php b/common/framework/helpers/dbhelper.php index aea3e6edd..72c26702f 100644 --- a/common/framework/helpers/dbhelper.php +++ b/common/framework/helpers/dbhelper.php @@ -33,6 +33,9 @@ class DBHelper extends \PDO */ public function prepare($statement, $driver_options = null) { + $start_time = microtime(true); + $db_class = DB::getInstance($this->_type); + try { if ($driver_options) @@ -48,6 +51,10 @@ class DBHelper extends \PDO } catch (\PDOException $e) { + $elapsed_time = microtime(true) - $start_time; + $db_class->addElapsedTime($elapsed_time); + $db_class->setError(-1, $e->getMessage()); + Debug::addQuery($db_class->getQueryLog($statement, $elapsed_time)); throw new DBError($e->getMessage(), 0, $e); } @@ -81,7 +88,7 @@ class DBHelper extends \PDO $elapsed_time = microtime(true) - $start_time; $db_class->addElapsedTime($elapsed_time); - Debug::addQuery($db_class->getQueryLog($statement, '', $elapsed_time)); + Debug::addQuery($db_class->getQueryLog($statement, $elapsed_time)); return $stmt; } @@ -109,7 +116,7 @@ class DBHelper extends \PDO $elapsed_time = microtime(true) - $start_time; $db_class->addElapsedTime($elapsed_time); - Debug::addQuery($db_class->getQueryLog($query, '', $elapsed_time)); + Debug::addQuery($db_class->getQueryLog($query, $elapsed_time)); return $result; } diff --git a/common/framework/helpers/dbstmthelper.php b/common/framework/helpers/dbstmthelper.php index 3f6a8e79d..7496e8cb2 100644 --- a/common/framework/helpers/dbstmthelper.php +++ b/common/framework/helpers/dbstmthelper.php @@ -42,7 +42,7 @@ class DBStmtHelper extends \PDOStatement $elapsed_time = microtime(true) - $start_time; $db_class->addElapsedTime($elapsed_time); - Debug::addQuery($db_class->getQueryLog($this->queryString, '', $elapsed_time)); + Debug::addQuery($db_class->getQueryLog($this->queryString, $elapsed_time)); } catch (\PDOException $e) { @@ -50,7 +50,7 @@ class DBStmtHelper extends \PDOStatement $elapsed_time = microtime(true) - $start_time; $db_class->addElapsedTime($elapsed_time); - Debug::addQuery($db_class->getQueryLog($this->queryString, '', $elapsed_time)); + Debug::addQuery($db_class->getQueryLog($this->queryString, $elapsed_time)); throw new DBError($e->getMessage(), 0, $e); } diff --git a/common/framework/parsers/dbquery/query.php b/common/framework/parsers/dbquery/query.php index 8d7a17d09..9f7e53135 100644 --- a/common/framework/parsers/dbquery/query.php +++ b/common/framework/parsers/dbquery/query.php @@ -130,7 +130,7 @@ class Query extends VariableBase elseif ($this->_column_list) { $result .= implode(', ', array_map(function($str) { - return '`' . $str . '`'; + return self::quoteName($str); }, $this->_column_list)); } else @@ -233,7 +233,7 @@ class Query extends VariableBase // Compose the INTO clause. if (count($this->tables)) { - $tables = $this->_arrangeTables($this->tables); + $tables = $this->_arrangeTables($this->tables, false); if ($tables !== '') { $result .= ' INTO ' . $tables; @@ -280,7 +280,7 @@ class Query extends VariableBase // Compose the INTO clause. if (count($this->tables)) { - $tables = $this->_arrangeTables($this->tables); + $tables = $this->_arrangeTables($this->tables, false); if ($tables !== '') { $result .= $tables; @@ -326,7 +326,7 @@ class Query extends VariableBase // Compose the FROM clause. if (count($this->tables)) { - $tables = $this->_arrangeTables($this->tables); + $tables = $this->_arrangeTables($this->tables, false); if ($tables !== '') { $result .= ' FROM ' . $tables; @@ -363,9 +363,10 @@ class Query extends VariableBase * Generate a FROM clause from a list of tables. * * @param array $tables + * @param bool $use_aliases * @return string */ - protected function _arrangeTables(array $tables): string + protected function _arrangeTables(array $tables, bool $use_aliases = true): string { // Initialize the result. $result = array(); @@ -376,7 +377,11 @@ class Query extends VariableBase // Subquery if ($table instanceof self) { - $tabledef = sprintf('(%s) AS `%s`', $table->getQueryString($this->_prefix, $this->_args), $table->alias); + $tabledef = '(' . $table->getQueryString($this->_prefix, $this->_args) . ')'; + if ($table->alias) + { + $tabledef .= ' AS `' . $table->alias . '`'; + } foreach ($table->getQueryParams() as $param) { $this->_params[] = $param; @@ -386,7 +391,11 @@ class Query extends VariableBase // Regular table else { - $tabledef = self::quoteName($this->_prefix . $table->name) . ($table->alias ? (' AS `' . $table->alias . '`') : ''); + $tabledef = self::quoteName($this->_prefix . $table->name); + if ($use_aliases && $table->alias && $table->alias !== ($this->_prefix . $table->name)) + { + $tabledef .= ' AS `' . $table->alias . '`'; + } } // Add join conditions @@ -485,7 +494,11 @@ class Query extends VariableBase } // Get the ordering (ASC or DESC). - if (isset($this->_args[$orderby->order_var])) + if (preg_match('/^(ASC|DESC)$/i', $orderby->order_var, $matches)) + { + $column_order = strtoupper($matches[1]); + } + elseif (isset($this->_args[$orderby->order_var])) { $column_order = preg_replace('/[^A-Z]/', '', strtoupper($this->_args[$orderby->order_var])); } diff --git a/common/framework/parsers/dbquery/variablebase.php b/common/framework/parsers/dbquery/variablebase.php index a80f0dd56..b8e534b42 100644 --- a/common/framework/parsers/dbquery/variablebase.php +++ b/common/framework/parsers/dbquery/variablebase.php @@ -282,7 +282,7 @@ class VariableBase { return [true, Query::quoteName($this->default)]; } - elseif (isset($this->column) && preg_match('/_srl$/', $this->column) && !ctype_digit($this->default)) + elseif (isset($this->column) && preg_match('/_srl$/', $this->column) && !is_numeric($this->default)) { return [true, Query::quoteName($this->default)]; } diff --git a/common/framework/parsers/dbqueryparser.php b/common/framework/parsers/dbqueryparser.php index e30332f5b..2c488c9a2 100644 --- a/common/framework/parsers/dbqueryparser.php +++ b/common/framework/parsers/dbqueryparser.php @@ -105,15 +105,16 @@ class DBQueryParser extends BaseParser } else { + $attribs = self::_getAttributes($tag); $column = new DBQuery\ColumnWrite; - $column->name = trim($tag['name']); - $column->operation = trim($tag['operation']) ?: 'equal'; - $column->var = trim($tag['var']) ?: null; - $column->default = trim($tag['default']) ?: null; - $column->not_null = self::_getAttributes($tag)['notnull'] ? true : false; - $column->filter = trim($tag['filter']) ?: null; - $column->minlength = intval(trim($tag['minlength']), 10); - $column->maxlength = intval(trim($tag['maxlength']), 10); + $column->name = $attribs['name']; + $column->operation = $attribs['operation'] ?: 'equal'; + $column->var = $attribs['var'] ?? null; + $column->default = $attribs['default'] ?? null; + $column->not_null = $attribs['notnull'] ? true : false; + $column->filter = $attribs['filter'] ?? null; + $column->minlength = intval($attribs['minlength'], 10); + $column->maxlength = intval($attribs['maxlength'], 10); $query->columns[] = $column; } } @@ -215,8 +216,15 @@ class DBQueryParser extends BaseParser $cond = new DBQuery\Condition; $cond->operation = $attribs['operation']; $cond->column = $attribs['column']; - $cond->var = $attribs['var'] ?? null; - $cond->default = $attribs['default'] ?? null; + if (isset($attribs['var']) && !isset($attribs['default']) && preg_match('/^\w+\.\w+$/', $attribs['var'])) + { + $cond->default = $attribs['var']; + } + else + { + $cond->var = $attribs['var'] ?? null; + $cond->default = $attribs['default'] ?? null; + } $cond->not_null = $attribs['notnull'] ? true : false; $cond->filter = $attribs['filter'] ?? null; $cond->minlength = intval($attribs['minlength'], 10); From 0f561b38142b53d013d1da92294e0f99d52bc266 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 30 Jun 2020 14:47:48 +0900 Subject: [PATCH 38/47] Deleted outdated dbxml_validator tests --- tools/dbxml_validator/connect_wrapper.php | 973 -------- .../tests/data/checkMissingPipe.xml | 23 - .../tests/data/dupListCount.xml | 24 - tools/dbxml_validator/tests/data/dupPage.xml | 25 - .../tests/data/dupPageCount.xml | 24 - .../dbxml_validator/tests/data/dupTblName.xml | 35 - .../tests/data/duplicateColumnsInSelect.xml | 9 - .../tests/data/emptyTableSubquery.xml | 27 - .../tests/data/joinedSubquery.xml | 38 - .../tests/data/joinedTableCondition.xml | 29 - .../tests/data/namedTableSubquery.xml | 36 - .../tests/data/schema/autoIncrementType.xml | 5 - .../tests/data/schema/fixedSizeType.xml | 6 - .../tests/data/schema/sizeVarcharType.xml | 6 - .../data/schema/warnings/autoIncrement.xml | 6 - .../tests/data/schema/warnings/tinyText.xml | 7 - .../tests/data/tableSubquery.xml | 36 - .../tests/data/unnamedTableSubquery.xml | 37 - tools/dbxml_validator/tests/data/update.xml | 26 - .../tests/data/wrongQueryId.xml | 6 - .../dbxml_validator/tests/xmlQueriesTest.php | 228 -- tools/dbxml_validator/validate.php | 2215 ----------------- tools/dbxml_validator/xml_colassign.xsd | 90 - tools/dbxml_validator/xml_create_table.xsd | 132 - tools/dbxml_validator/xml_datachange.xsd | 100 - tools/dbxml_validator/xml_delete.xsd | 53 - tools/dbxml_validator/xml_insert.xsd | 43 - tools/dbxml_validator/xml_insert_generic.xsd | 45 - tools/dbxml_validator/xml_insert_select.xsd | 44 - tools/dbxml_validator/xml_query.xsd | 801 ------ tools/dbxml_validator/xml_select.xsd | 57 - tools/dbxml_validator/xml_update.xsd | 45 - tools/dbxml_validator/xsd_types.dia | Bin 6512 -> 0 bytes tools/phpDoc/phpdoc.dist.xml | 22 - 34 files changed, 5253 deletions(-) delete mode 100644 tools/dbxml_validator/connect_wrapper.php delete mode 100644 tools/dbxml_validator/tests/data/checkMissingPipe.xml delete mode 100644 tools/dbxml_validator/tests/data/dupListCount.xml delete mode 100644 tools/dbxml_validator/tests/data/dupPage.xml delete mode 100644 tools/dbxml_validator/tests/data/dupPageCount.xml delete mode 100644 tools/dbxml_validator/tests/data/dupTblName.xml delete mode 100644 tools/dbxml_validator/tests/data/duplicateColumnsInSelect.xml delete mode 100644 tools/dbxml_validator/tests/data/emptyTableSubquery.xml delete mode 100644 tools/dbxml_validator/tests/data/joinedSubquery.xml delete mode 100644 tools/dbxml_validator/tests/data/joinedTableCondition.xml delete mode 100644 tools/dbxml_validator/tests/data/namedTableSubquery.xml delete mode 100644 tools/dbxml_validator/tests/data/schema/autoIncrementType.xml delete mode 100644 tools/dbxml_validator/tests/data/schema/fixedSizeType.xml delete mode 100644 tools/dbxml_validator/tests/data/schema/sizeVarcharType.xml delete mode 100644 tools/dbxml_validator/tests/data/schema/warnings/autoIncrement.xml delete mode 100644 tools/dbxml_validator/tests/data/schema/warnings/tinyText.xml delete mode 100644 tools/dbxml_validator/tests/data/tableSubquery.xml delete mode 100644 tools/dbxml_validator/tests/data/unnamedTableSubquery.xml delete mode 100644 tools/dbxml_validator/tests/data/update.xml delete mode 100644 tools/dbxml_validator/tests/data/wrongQueryId.xml delete mode 100644 tools/dbxml_validator/tests/xmlQueriesTest.php delete mode 100644 tools/dbxml_validator/validate.php delete mode 100644 tools/dbxml_validator/xml_colassign.xsd delete mode 100644 tools/dbxml_validator/xml_create_table.xsd delete mode 100644 tools/dbxml_validator/xml_datachange.xsd delete mode 100644 tools/dbxml_validator/xml_delete.xsd delete mode 100644 tools/dbxml_validator/xml_insert.xsd delete mode 100644 tools/dbxml_validator/xml_insert_generic.xsd delete mode 100644 tools/dbxml_validator/xml_insert_select.xsd delete mode 100644 tools/dbxml_validator/xml_query.xsd delete mode 100644 tools/dbxml_validator/xml_select.xsd delete mode 100644 tools/dbxml_validator/xml_update.xsd delete mode 100644 tools/dbxml_validator/xsd_types.dia delete mode 100644 tools/phpDoc/phpdoc.dist.xml diff --git a/tools/dbxml_validator/connect_wrapper.php b/tools/dbxml_validator/connect_wrapper.php deleted file mode 100644 index f9ebacf48..000000000 --- a/tools/dbxml_validator/connect_wrapper.php +++ /dev/null @@ -1,973 +0,0 @@ - */ -/** @file - vi: ts=4 - - Extends XE db classes to allow parsing methods to work in the absence of - a real db connection for the db type. - - Included by XML Query/Schema Language validator -*/ - -/** - @brief - @developer - */ -class DBMysqlConnectWrapper extends DBMysql -{ - public $queries = ''; - - /** - @brief - @developer - @return - @access - */ - public function __construct() - { - $this->db_type = 'mysql'; - $this->_setDBInfo(); // Context::get() should indicate a mysql db - } - - /** - @brief - @developer - @return - @access - */ - public function create() - { - return new DBMysqlConnectWrapper(); - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassStart() - { - } - - /** - @brief - @developer - @return - @access - @param $query - */ - public function actStart($query) - { - } - - /** - @brief - @developer - @return - @access - */ - public function actFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function isSupported() - { - // No need to actually check for 'mysql_connect' function - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function __connect($connection) - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _afterConnect($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _close($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $type - @param $indx - */ - public function close($type = 'master', $indx = NULL) - { - } - - /** - @brief - @developer - @return - @access - */ - public function _begin() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _rollback() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _commit() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $query - @param $connection - */ - public function __query($query, $connection) - { - $this->queries .= "\n" . $query; - - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $result - @param $arrayIndexEndValue - */ - public function _fetch($result, $arrayIndexEndValue = NULL) - { - return new Any_prop_obj_base(); - } - - /** - @brief - @developer - @return - @access - @param $target_name - */ - public function isTableExists($target_name) - { - parent::isTableExists($target_name); - - return FALSE; - } - - /** - @brief - @developer - @return - @access - */ - public function db_insert_id() - { - return NULL; - } - - /** - @brief - @developer - @return - @access - @param $result - */ - public function db_fetch_object(&$result) - { - return new Any_prop_obj_base(); - } -} - -/** - @brief - @developer - */ -class DBMysqliConnectWrapper extends DBMysqli -{ - public $queries = ''; - - /** - @brief - @developer - @return - @access - */ - public function __construct() - { - $this->db_type = 'mysqli'; - $this->_setDBInfo(); // Context::get() should indicate a mysqli db - } - - /** - @brief - @developer - @return - @access - */ - public function create() - { - return new DBMysqlConnectWrapper(); - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassStart() - { - } - - /** - @brief - @developer - @return - @access - @param $query - */ - public function actStart($query) - { - } - - /** - @brief - @developer - @return - @access - */ - public function actFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function isSupported() - { - // No need to actually check for 'mysql_connect' function - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $target_name - */ - public function isTableExists($target_name) - { - parent::isTableExists($target_name); - - return FALSE; - } - - // use old mysql escape function, since the mysqli one uses - // the connection resource (to get the current character set) - /** - @brief - @developer - @return - @access - @param $string - */ - public function addQuotes($string) - { - if(version_compare(PHP_VERSION, "5.4.0", "<") && get_magic_quotes_gpc()) - { - $string = stripslashes(str_replace("\\", "\\\\", $string)); - } - - if(!is_numeric($string)) - { - $string = @mysql_real_escape_string($string); - } - - return $string; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function __connect($connection) - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _afterConnect($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _close($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $type - @param $indx - */ - public function close($type = 'master', $indx = NULL) - { - } - - /** - @brief - @developer - @return - @access - */ - public function _begin() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _rollback() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _commit() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $query - @param $connection - */ - public function __query($query, $connection) - { - $this->queries .= "\n" . $query; - - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $result - @param $arrayIndexEndValue - */ - public function _fetch($result, $arrayIndexEndValue = NULL) - { - return new Any_prop_obj_base(); - } - - /** - @brief - @developer - @return - @access - */ - public function db_insert_id() - { - return NULL; - } - - /** - @brief - @developer - @return - @access - @param $result - */ - public function db_fetch_object(&$result) - { - return new Any_prop_obj_base(); - } -} - -/** - @brief - @developer - */ -class DBCubridConnectWrapper extends DBCubrid -{ - public $queries = ''; - - /** - @brief - @developer - @return - @access - */ - public function __construct() - { - $this->db_type = 'cubrid'; - $this->_setDBInfo(); // Context::get() should indicate a CUBRID db - } - - /** - @brief - @developer - @return - @access - */ - public function create() - { - return new DBMysqlConnectWrapper(); - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassStart() - { - } - - /** - @brief - @developer - @return - @access - @param $query - */ - public function actStart($query) - { - } - - /** - @brief - @developer - @return - @access - */ - public function _makeSequence() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function actFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function isSupported() - { - // No need to actually check for 'cubrid_connect' function - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $target_name - */ - public function isTableExists($target_name) - { - try - { - parent::isTableExists($target_name); - } - catch (Exception $ex) - { - } - - return FALSE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function __connect($connection) - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _afterConnect($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _close($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $type - @param $indx - */ - public function close($type = 'master', $indx = NULL) - { - } - - /** - @brief - @developer - @return - @access - */ - public function _begin() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _rollback() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _commit() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $query - @param $connection - */ - public function __query($query, $connection) - { - $this->queries .= "\n" . $query; - - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $result - @param $arrayIndexEndValue - */ - public function _fetch($result, $arrayIndexEndValue = NULL) - { - return new Any_prop_obj_base(); - } - - /** - @brief - @developer - @return - @access - */ - public function db_insert_id() - { - return NULL; - } - - /** - @brief - @developer - @return - @access - */ - public function &db_fetch_object() - { - return new Any_prop_obj_base(); - } -} - -/** - @brief - @developer - */ -class DBMssqlConnectWrapper extends DBMssql -{ - public $queries = ''; - - /** - @brief - @developer - @return - @access - */ - public function __construct() - { - $this->db_type = 'mssql'; - $this->_setDBInfo(); // Context::get() should indicate a MS Sql db - } - - /** - @brief - @developer - @return - @access - */ - public function create() - { - return new DBMssqlConnectWrapper(); - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassStart() - { - } - - /** - @brief - @developer - @return - @access - @param $query - */ - public function actStart($query) - { - } - - /** - @brief - @developer - @return - @access - */ - public function actFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function actDBClassFinish() - { - } - - /** - @brief - @developer - @return - @access - */ - public function isSupported() - { - // No need to actually check for 'mssql_connect' function - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $target_name - */ - public function isTableExists($target_name) - { - parent::isTableExists($target_name); - - return FALSE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function __connect($connection) - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _afterConnect($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $connection - */ - public function _close($connection) - { - } - - /** - @brief - @developer - @return - @access - @param $type - @param $indx - */ - public function close($type = 'master', $indx = NULL) - { - } - - /** - @brief - @developer - @return - @access - */ - public function _begin() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _rollback() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public function _commit() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $query - @param $connection - */ - public function __query($query, $connection) - { - if($this->queries) - { - $this->queries .= ";\n"; - } - - $this->queries .= $query; - - return TRUE; - } - - /** - @brief - @developer - @return - @access - @param $result - @param $arrayIndexEndValue - */ - public function _fetch($result, $arrayIndexEndValue = NULL) - { - return new Any_prop_obj_base(); - } - - /** - @brief - @developer - @return - @access - */ - public function db_insert_id() - { - return NULL; - } - - /** - @brief - @developer - @return - @access - */ - public function &db_fetch_object() - { - return new Any_prop_obj_base(); - } -} - -/* End of file connect_wrapper.php */ -/* Location: tools/dbxml_validator/connect_wrapper.php */ diff --git a/tools/dbxml_validator/tests/data/checkMissingPipe.xml b/tools/dbxml_validator/tests/data/checkMissingPipe.xml deleted file mode 100644 index af12a89b5..000000000 --- a/tools/dbxml_validator/tests/data/checkMissingPipe.xml +++ /dev/null @@ -1,23 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/dupListCount.xml b/tools/dbxml_validator/tests/data/dupListCount.xml deleted file mode 100644 index 41c1314e2..000000000 --- a/tools/dbxml_validator/tests/data/dupListCount.xml +++ /dev/null @@ -1,24 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/dupPage.xml b/tools/dbxml_validator/tests/data/dupPage.xml deleted file mode 100644 index fc6047fce..000000000 --- a/tools/dbxml_validator/tests/data/dupPage.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/dupPageCount.xml b/tools/dbxml_validator/tests/data/dupPageCount.xml deleted file mode 100644 index bec786fb5..000000000 --- a/tools/dbxml_validator/tests/data/dupPageCount.xml +++ /dev/null @@ -1,24 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/dupTblName.xml b/tools/dbxml_validator/tests/data/dupTblName.xml deleted file mode 100644 index 8ad435e3c..000000000 --- a/tools/dbxml_validator/tests/data/dupTblName.xml +++ /dev/null @@ -1,35 +0,0 @@ - - -
-
- -
- - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/duplicateColumnsInSelect.xml b/tools/dbxml_validator/tests/data/duplicateColumnsInSelect.xml deleted file mode 100644 index f568ce379..000000000 --- a/tools/dbxml_validator/tests/data/duplicateColumnsInSelect.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/emptyTableSubquery.xml b/tools/dbxml_validator/tests/data/emptyTableSubquery.xml deleted file mode 100644 index 8a6558a0c..000000000 --- a/tools/dbxml_validator/tests/data/emptyTableSubquery.xml +++ /dev/null @@ -1,27 +0,0 @@ - - -
-
-
-
- - - - - - - - - - - - - - - - - - -
- - diff --git a/tools/dbxml_validator/tests/data/joinedSubquery.xml b/tools/dbxml_validator/tests/data/joinedSubquery.xml deleted file mode 100644 index 23f0c3f60..000000000 --- a/tools/dbxml_validator/tests/data/joinedSubquery.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - -
- -
- - - - - - - - -
-
- - - - - - - - - - - - - - - - - - -
- - - diff --git a/tools/dbxml_validator/tests/data/joinedTableCondition.xml b/tools/dbxml_validator/tests/data/joinedTableCondition.xml deleted file mode 100644 index d048320f0..000000000 --- a/tools/dbxml_validator/tests/data/joinedTableCondition.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- - - - diff --git a/tools/dbxml_validator/tests/data/namedTableSubquery.xml b/tools/dbxml_validator/tests/data/namedTableSubquery.xml deleted file mode 100644 index f68ef1574..000000000 --- a/tools/dbxml_validator/tests/data/namedTableSubquery.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - -
- -
- - - - - - - - -
-
- - - - - - - - - - - - - - - - - - -
- diff --git a/tools/dbxml_validator/tests/data/schema/autoIncrementType.xml b/tools/dbxml_validator/tests/data/schema/autoIncrementType.xml deleted file mode 100644 index 23de09cac..000000000 --- a/tools/dbxml_validator/tests/data/schema/autoIncrementType.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - -
diff --git a/tools/dbxml_validator/tests/data/schema/fixedSizeType.xml b/tools/dbxml_validator/tests/data/schema/fixedSizeType.xml deleted file mode 100644 index a03fca18e..000000000 --- a/tools/dbxml_validator/tests/data/schema/fixedSizeType.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - -
- diff --git a/tools/dbxml_validator/tests/data/schema/sizeVarcharType.xml b/tools/dbxml_validator/tests/data/schema/sizeVarcharType.xml deleted file mode 100644 index e3fab1122..000000000 --- a/tools/dbxml_validator/tests/data/schema/sizeVarcharType.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - -
- diff --git a/tools/dbxml_validator/tests/data/schema/warnings/autoIncrement.xml b/tools/dbxml_validator/tests/data/schema/warnings/autoIncrement.xml deleted file mode 100644 index 0e25d6a55..000000000 --- a/tools/dbxml_validator/tests/data/schema/warnings/autoIncrement.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - -
- diff --git a/tools/dbxml_validator/tests/data/schema/warnings/tinyText.xml b/tools/dbxml_validator/tests/data/schema/warnings/tinyText.xml deleted file mode 100644 index a85c74bf9..000000000 --- a/tools/dbxml_validator/tests/data/schema/warnings/tinyText.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - -
- diff --git a/tools/dbxml_validator/tests/data/tableSubquery.xml b/tools/dbxml_validator/tests/data/tableSubquery.xml deleted file mode 100644 index 6d4d5b1cd..000000000 --- a/tools/dbxml_validator/tests/data/tableSubquery.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - -
- -
- - - - - - - - -
-
- - - - - - - - - - - - - - - - - - -
- diff --git a/tools/dbxml_validator/tests/data/unnamedTableSubquery.xml b/tools/dbxml_validator/tests/data/unnamedTableSubquery.xml deleted file mode 100644 index 4f7ad154e..000000000 --- a/tools/dbxml_validator/tests/data/unnamedTableSubquery.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - -
- -
- - - - - - - - -
-
- - - - - - - - - - - - - - - - - - -
- - diff --git a/tools/dbxml_validator/tests/data/update.xml b/tools/dbxml_validator/tests/data/update.xml deleted file mode 100644 index 5c2803ec6..000000000 --- a/tools/dbxml_validator/tests/data/update.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - -
- - - - - - - - - - - - diff --git a/tools/dbxml_validator/tests/data/wrongQueryId.xml b/tools/dbxml_validator/tests/data/wrongQueryId.xml deleted file mode 100644 index 53c41a17c..000000000 --- a/tools/dbxml_validator/tests/data/wrongQueryId.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - -
- - diff --git a/tools/dbxml_validator/tests/xmlQueriesTest.php b/tools/dbxml_validator/tests/xmlQueriesTest.php deleted file mode 100644 index 49b5f9c5e..000000000 --- a/tools/dbxml_validator/tests/xmlQueriesTest.php +++ /dev/null @@ -1,228 +0,0 @@ - */ - -if (!defined('__DIR__')) - define('__DIR__', realpath(dirname(__FILE__))); - -/** The tests here are meant only for the built-in checks in validator.php, - and not for the entire syntax expressed by the .xsd files. */ -class XmlQueriesTest extends PHPUnit_Framework_TestCase -{ - // taken from validator.php - - const RETCODE_VALIDATOR_INTERNAL = 60; - const RETCODE_GENERIC_XML_SYNTAX = 50; - const RETCODE_QUERY_ELEMENT = 40; - const RETCODE_XSD_VALIDATION = 30; - const RETCODE_BUILTIN_CHECKS = 20; - const RETCODE_DB_SCHEMA_MATCH =10; // no schema match is currently implemented. - const RETCODE_SUCCESS = 0; - - public $validator_cmd; - - public function setUp() - { - $this->validator_cmd = "php " . escapeshellarg(__DIR__ . '/../validate.php') . " "; - } - - // recursive glob - // On Windows glob() is case-sensitive. - public function globr($sDir, $sPattern, $nFlags = NULL) - { - // Get the list of all matching files currently in the - // directory. - - $aFiles = glob("$sDir/$sPattern", $nFlags); - - $this->assertTrue(is_array($aFiles), 'directory listing failed.'); - - $aDirs = glob("$sDir/*", GLOB_ONLYDIR | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_ERR); - $this->assertTrue(is_array($aDirs), 'directory listing failed.'); - - foreach ($aDirs as $sSubDir) - { - if ($sSubDir != '.' && $sSubDir != '..') - { - $aSubFiles = $this->globr($sSubDir, $sPattern, $nFlags); - $aFiles = array_merge($aFiles, $aSubFiles); - } - } - - // return merged array with all (recursive) files - return $aFiles; - } - - /** Tests all XML Query and Schema Language files (in all modules/addons/widgets) in XE */ - public function invoke_testReleasedXMLLangFiles - ( - $released_files, - $expected_return_code, - $validator_args = '' - ) - { - // this file is in tools/dbxml_validator/tests - $xe_dir = __DIR__ . '/../../..'; - - $this->assertTrue(file_exists($xe_dir . '/index.php'), 'XE directory not found'); - - $cnt = 0; - $cmd = $this->validator_cmd; - $xml_files = array(); - - foreach ($released_files as $released_file_mask) - $xml_files = - array_merge - ( - $xml_files, - $this->globr - ( - $xe_dir, - $released_file_mask, - GLOB_NOSORT | GLOB_NOESCAPE | GLOB_ERR - ) - ); - - while ($cnt < count($xml_files)) - { - $cmd = $this->validator_cmd . $validator_args; - - // Validate 50 files at once - foreach (array_slice($xml_files, $cnt, 50) as $xml_file) - $cmd .= " " . escapeshellarg($xml_file); - - exec($cmd . ' 2>&1', $validator_output, $return_code); - - $output_text = trim(trim(implode("\n", $validator_output)), "\n"); - - // Validator should not crash/exit-with-an-error. - $this->assertLessThanOrEqual - ( - $expected_return_code, - $return_code, - "{$cmd}\n\n{$output_text}\nValidator returned code {$return_code}." - ); - - $cnt += 50; - } - } - - public function testReleasedXMLQueryLangFiles() - { - $this->invoke_testReleasedXMLLangFiles - ( - array('queries/*.xml', 'xml_query/*.xml'), - self::RETCODE_QUERY_ELEMENT - ); - - $this->markTestIncomplete('XML Query Language files should be fixed first.'); - } - - public function testReleasedXMLSchemaLangFiles() - { - $this->invoke_testReleasedXMLLangFiles - ( - array('schemas/*.xml'), - self::RETCODE_BUILTIN_CHECKS, - ' --schema-language' - ); - - $this->markTestIncomplete('XML Schema Language files should be fixed first.'); - } - - public function invoke_testInvalidXmlFiles($filename, $err_code, $args = '') - { - $cmd = $this->validator_cmd . ' '. $args . ' ' . escapeshellarg($filename); - $validator_output = array(); - $return_code = 0; - - exec($cmd . '2>&1', $validator_output, $return_code); - - $output_text = trim(trim(implode("\n", $validator_output)), "\n"); - - // Validator should not crash/exit-with-an-error. - $this->assertEquals - ( - $err_code, - $return_code, - "{$cmd}\n{$output_text}\nValidator returned code {$return_code}." - ); - - // Validator should output some error on the test files - $basefilename = basename($filename); - $this->assertNotEmpty($output_text, "Error reporting failed for {$basefilename} validation."); - - } - - public function testInvalidQueryId() - { - return $this->invoke_testInvalidXmlFiles(__DIR__.'/data/wrongQueryId.xml', self::RETCODE_QUERY_ELEMENT); - } - - /** - * @dataProvider getFilesList - */ - public function testInvalidXMLQueryFiles($filename) - { - return $this->invoke_testInvalidXmlFiles($filename, self::RETCODE_BUILTIN_CHECKS); - } - - public function getDirFilesList($dir_name) - { - $output = array(); - - $dir = opendir(__DIR__ . '/' . $dir_name); - - if ($dir) - { - $entry = readdir($dir); - - while ($entry !== FALSE) - { - $fname = __DIR__ . '/' . $dir_name .'/' . $entry; - - if (!is_dir($fname)&& $entry != 'wrongQueryId.xml') - $output[] = array($fname); - - $entry = readdir($dir); - } - - closedir($dir); - } - else - $this->assertFalse(TRUE); - - return $output; - } - - public function getFilesList() - { - return $this->getDirFilesList('data'); - } - - public function getSchemaFilesList() - { - return $this->getDirFilesList('data/schema'); - } - - public function getSchemaWarningFilesList() - { - return $this->getDirFilesList('data/schema/warnings'); - } - - /** - * @dataProvider getSchemaFilesList - */ - public function testInvalidXMLSchemaFiles($filename) - { - return $this->invoke_testInvalidXmlFiles($filename, self::RETCODE_BUILTIN_CHECKS, '--schema-language'); - } - - /** - * @dataProvider getSchemaWarningFilesList - */ - public function testWarningXMLSchemaFiles($filename) - { - return $this->invoke_testInvalidXmlFiles($filename, self::RETCODE_SUCCESS, '--schema-language'); - } -} - diff --git a/tools/dbxml_validator/validate.php b/tools/dbxml_validator/validate.php deleted file mode 100644 index 13b58097e..000000000 --- a/tools/dbxml_validator/validate.php +++ /dev/null @@ -1,2215 +0,0 @@ -#!/bin/env php - */ -/** - vi:set ts=4: - @file - - Script to validate a query or a SQL statement written in the - XpressEngine XML Query Language or the XML Schema language. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - @Author: Arnia Software - @Date: 9 apr 2012 - - The validation is based on, and is meant to model, the behavior exposed - by the php classes in classes/xml/xmlquery/ and class/db/queryparts/ - in the XE installation directory. - - Usage: - validate.php query-file.xml query-file.xml ... - or - validate.php schema-definition.xsd query-file.xml ... - */ - -error_reporting(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); -ini_set('display_errors', 'stderr'); - -/** - @brief callback to turn a php error into a php exception - So now any error interrupts or terminates script execution - @developer Arnia Software - @param $errno - php error number - @param $errstr - php error string - @param $errfile - file name - @param $errline - line no - @return none - */ -function exception_error_handler($errno, $errstr, $errfile, $errline) -{ - // exit on error - throw new ErrorException($errstr, 0, $errno, $errfile, $errline); -} - -// set_error_handler("exception_error_handler"); - -// Error reporting classes/functions - -/** - @brief Exception class for user error messages. - - @developer Arnia Software - */ -class ErrorMessage extends Exception -{ -} - -/** - @brief Error message class to signal and carry - the command-line usage description (string) for the script - - @developer Arnia Software - */ -class SyntaxError extends ErrorMessage -{ -} - -/** - @brief Error in an XML query - @developer Arnia Software - */ -class XmlSchemaError extends ErrorMessage -{ - public $xml_file; - public $xml_line_no; - public $xml_message; - - /** - @brief Composes a message in the format: - -
-		file_name (line_no):
-			message
-		
- @developer Arnia Software - - @return - @param $file - @param $line_no - @param $message - @access - */ - public function __construct($file, $line_no, $message) - { - parent::__construct("{$file}({$line_no}):\n\t$message"); - - $this->xml_file = $file; - $this->xml_line_no = $line_no; - $this->xml_message = $message; - } -} - -/** - @brief Clean up libxml errors list when going out of scope (on the destructor) - @developer Arnia Software - */ -class LibXmlClearErrors -{ - /** - @brief Clear libXML errors - @developer - @return - @access - */ - public function __destruct() - { - libxml_clear_errors(); - } -} - -/** - @brief - @developer - @return - @param $filename - @param $throw_error -*/ -function libXmlDisplayError($filename = NULL, $throw_error = FALSE) -{ - // set up clean-up call to libxml_clear_errors() - $libXmlClearErrors = new LibXmlClearErrors(); - - $libXmlErrors = libxml_get_errors(); - - if(count($libXmlErrors)) - { - if(!$filename) - { - $filename = $libXmlErrors[0]->file; - } - - $msg = ''; - - foreach($libXmlErrors as $libXmlError) - { - $msg .= "{$libXmlError->file}({$libXmlError->line}):\n\t {$libXmlError->message}"; - } - - if($throw_error) - { - throw new ErrorMessage($msg); - } - else - { - fwrite(STDERR, $msg . "\n"); - } - } - else - { - if($throw_error) - { - throw new ErrorMessage('Schema validation failed.'); - } - } -} - -/** - Checks an XML node for duplicate descendants of a give tag. - Throws XmlSchemaError if duplicates found. - @brief - @developer - @return - @param $xml_file - @param $node - @param $child_tag - */ -function checkDuplicateDescendants($xml_file, $node, $child_tag) -{ - $children = $node->getElementsByTagName($child_tag); - - if($children->length > 1) - { - throw - new XmlSchemaError( - $xml_file, - $children->item(1)->getLineNo(), - "Duplicate <{$child_tag}> elements." - ); - } -} - -/** - Checks the XML child nodes for unique/key values of one (or more) - attribute(s) - - @param $xml_file - Name of file with the XML node to be checked. Used in the error - messages. - @param $node - The XML node with the children to be checked. - @param $child_tags - Array with tag names for the children elements - @param $attr_tags - Array with names of attributes to be checked. If multiple attributes - are given, than the first one that is present on a child is included - in the check. - @param $key - True if child elements are required to expose at least one of the - attribute. False if only the child nodes with some of the - attributes present are to be checked. - @brief - @developer - @return -*/ -function checkUniqueKey($xml_file, $node, $child_tags, $attr_tags, $key) -{ - $key_values = array(); - - foreach($node->childNodes as $child_node) - { - if($child_node->nodeType == XML_ELEMENT_NODE - && - in_array($child_node->tagName, $child_tags)) - { - $key_value = NULL; - - foreach($attr_tags as $attr_tag) - { - if($child_node->hasAttribute($attr_tag)) - { - $key_value = $child_node->getAttribute($attr_tag); - - if(array_key_exists($key_value, $key_values)) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - "Duplicate {$attr_tag} found in <{$node->tagName}>." - ); - } - - $key_values[$key_value] = TRUE; - break; - } - } - - if(!$key_value && $key) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - "<{$child_node->tagName}>: at least one of the following attributes is expected: " - . - implode(', ', $attr_tags) . '.' - ); - } - } - } -} - -/** - Checks a SQL table-expression in the FROM clause. The table - should be either: - - a named table. If the table is the right table of a join, - then the join conditions should be given as content. - - a sub-query. No table name should be given, table alias - should be present and query attribute should be present - and have the value "true". Content should include at - least a select list or a table specification - - @brief - @developer - @return - @param $xml_file - @param $table_element - */ -function checkTableExpression($xml_file, $table_element) -{ - $table_name = NULL; - $join_type = NULL; - - if($table_element->hasAttribute('name')) - { - $table_name = $table_element->getAttribute('name'); - } - - if($table_element->hasAttribute('type')) - { - $join_type = $table_element->getAttribute('type'); - } - - if($table_element->getAttribute('query') == 'true') - { - if($table_name !== NULL) - { - throw - new XmlSchemaError( - $xml_file, - $table_element->getLineNo(), - 'Subqueries should only use aliases, not names' - ); - } - - if($join_type !== NULL) - { - throw - new XmlSchemaError( - $xml_file, - $table_element->getLineNo(), - 'Currently subqueries may not be used as ' - . - 'the right side table in a join' - ); - } - - // table alias is already checked by the unique key constraint on - // the (alias or name) key on the tables element - - // check contents for a select list or a table-specification - $has_query_clauses = FALSE; - - foreach($table_element->childNodes as $query_clause) - { - if($query_clause->nodeType == XML_ELEMENT_NODE - && - ( - $query_clause->tagName == 'columns' - || - $query_clause->tagName == 'tables')) - { - $has_query_clauses = TRUE; - } - } - - if(!$has_query_clauses) - { - throw - new XmlSchemaError( - $xml_file, - $table_element->getLineNo(), - 'Subquery tables should have at least a select list or a table specification.' - . - "\nANSI SQL-99 declares the table specification as required." - ); - } - } - else - { - // base table or view - - if($join_type !== NULL) - { - $has_conditions_element = FALSE; - - foreach($table_element->childNodes as $child_node) - { - if($child_node->nodeType == XML_ELEMENT_NODE) - { - if($child_node->tagName == 'conditions') - { - if($has_conditions_element) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - 'Duplicate elements.' - ); - } - else - { - $has_conditions_element = TRUE; - } - } - else - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - ' element must be the only content for a joined
.' - ); - } - } - } - - if(!$has_conditions_element) - { - throw - new XmlSchemaError( - $xml_file, - $table_element->getLineNo(), - 'Expected element as content.' - ); - } - } - else - { - foreach($table_element->childNodes as $child_node) - { - if($child_node->nodeType == XML_ELEMENT_NODE) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - '
element can only have content if it is a sub-query or is joined.' - ); - } - } - } - } -} - -/** - All table names and aliases should be distinct throughout - the element. - - Subquery tables should be valid queries. - - @brief - @developer - @return - @param $xml_file - @param $tables_element - */ -function checkTablesClause($xml_file, $tables_element) -{ - checkUniqueKey - ( - $xml_file, - $tables_element, - array('table'), // child elements to be checked - array('alias', 'name'), // attributes to be checked - TRUE // attributes are required - ); - - foreach($tables_element->childNodes as $table) - { - if($table->nodeType == XML_ELEMENT_NODE - && - $table->tagName == 'table') - { - checkTableExpression($xml_file, $table); - - if($table->getAttribute('query') == 'true') - { - validate_select_query($xml_file, $table); // recursive call - } - } - } -} - -/** - Table columns in a select-list should be unique. This is not a - requirement by the SQL language, and it can be commented out - below, but it is still common sense. - - Some of the "columns" here are actually small expressions, but - they can still be included literally in the unique constraint. - - @brief - @developer - @return - @param $xml_file - @param $columns_element - */ -function checkSelectListClause($xml_file, $columns_element) -{ - checkUniqueKey - ( - $xml_file, - $columns_element, - array('column', 'query'), // child elements - array('alias', 'name'), // attributes - FALSE // ignore if no attributes present - ); -} - -/** - Check that attributes for variable contents validation - (filter, notnull, minlength, maxlength) are present only if - the var attribute is present. - @brief - @developer - @return - @param $xml_file - @param $container_element - @param $child_tag -*/ -function checkVarContentsValidation - ( - $xml_file, - $container_element, - $child_tag - ) -{ - static - $key_attr = 'var'; - - static - $var_attrs = - array - ( - 'filter', 'notnull', 'minlength', - 'maxlength' - ); - - foreach($container_element->childNodes as $child_node) - { - if($child_node->nodeType == XML_ELEMENT_NODE - && - $child_node->tagName == $child_tag) - { - if(!$child_node->hasAttribute($key_attr)) - { - foreach($var_attrs as $var_attr) - { - if($child_node->hasAttribute($var_attr)) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - "<{$child_node->tagName}>: Attribute '{$var_attr}' " - . - "should only be used with the '{$key_attr}' attribute." - ); - } - } - } - } - } -} - -/** - Checks that a subquery condition does not have a var or default attribute. - - @brief - @developer - @return - @param $xml_file - @param $condition -*/ -function checkConditionElement($xml_file, $condition) -{ - $child_query_node = FALSE; - $has_var_attribute = $condition->hasAttribute('var') || $condition->hasAttribute('default'); - $query_line_no = -1; - - foreach($condition->childNodes as $query_node) - { - if($query_node->nodeType == XML_ELEMENT_NODE - && - $query_node->tagName == 'query') - { - validate_select_query($xml_file, $query_node); - - $child_query_node = TRUE; - $query_line_no = $query_node->getLineNo(); - } - } - - if($child_query_node && $has_var_attribute) - { - throw - new XmlSchemaError( - $xml_file, - $query_line_no, - " element found when has a 'var' or 'default' attribute." - ); - } - - if(!($child_query_node || $has_var_attribute)) - { - throw - new XmlSchemaError( - $xml_file, - $condition->getLineNo(), - ": either a child, 'var' attribute or 'default' attribute expected." - ); - } -} - - -/** - Checks that conditions have the pipe attribute, and that variable-contents-validation - attributes are only present if var attribute is present. - - Also recurses into condition groups and expression subqueries - @brief - @developer - @return - @param $xml_file - @param $conditions - */ -function checkConditionsGroup($xml_file, $conditions) -{ - $first_child = TRUE; - - foreach($conditions->childNodes as $child_node) - { - if($child_node->nodeType == XML_ELEMENT_NODE) - { - // check for 'pipe' attribute - if($first_child) - { - $first_child = FALSE; - } - else - { - if(!$child_node->hasAttribute('pipe')) - { - throw - new XmlSchemaError( - $xml_file, - $child_node->getLineNo(), - 'Attribute pipe expected for all but the first element' - . - " in <{$conditions->tagName}> content." - ); - } - } - - // recurse in condition groups/queries - if($child_node->tagName == 'group') - { - checkConditionsGroup($xml_file, $child_node); - } - else - { - if($child_node->tagName == 'query') - { - validate_select_query($xml_file, $child_node); - } - else - { - if($child_node->tagName == 'condition') - { - checkConditionElement($xml_file, $child_node); - } - } - } - } - } - - // check variable contents validation attributes - checkVarContentsValidation($xml_file, $conditions, 'condition'); -} - -/** - Ensure at most one , and - elements are present. There can be any number of - elements listed. - - @brief - @developer - @return - @param $xml_file - @param $navigation_element - */ -function checkNavigationClauses($xml_file, $navigation_element) -{ - foreach(array('list_count', 'page_count', 'page') - as - $navigation_el) - { - checkDuplicateDescendants - ( - $xml_file, - $navigation_element, - $navigation_el - ); - } -} - -/** - Additional checks to validate a query XML, that can not - be properly expressed in the schema definition (.xsd) file - for the query. - - Most likely the conditions explicitly coded and checked for - here can also be expressed as XPath queries. - - @brief - @developer - @return - @param $xml_file - @param $query_element - */ -function validate_select_query($xml_file, $query_element) -{ - foreach($query_element->childNodes as $select_clause) - { - if($select_clause->nodeType == XML_ELEMENT_NODE) - { - switch($select_clause->tagName) - { - case 'columns': - checkSelectListClause($xml_file, $select_clause); - break; - - case 'tables': - checkTablesClause($xml_file, $select_clause); - break; - - case 'conditions': - checkConditionsGroup($xml_file, $select_clause); - break; - - case 'navigation': - checkNavigationClauses($xml_file, $select_clause); - break; - } - } - } -} - -/** - @brief - @developer - @return - @param $xml_file - @param $query_element - */ -function validate_update_query($xml_file, $query_element) -{ - foreach($query_element->childNodes as $update_clause) - { - if($update_clause->nodeType == XML_ELEMENT_NODE) - { - switch($update_clause->tagName) - { - case 'tables': - checkTablesClause($xml_file, $update_clause); - break; - - case 'conditions': - checkConditionsGroup($xml_file, $update_clause); - break; - } - } - } -} - -/** - @brief - @developer - @param $xml_file - @param $query_element - @return - */ -function validate_delete_query($xml_file, $query_element) -{ - foreach($query_element->childNodes as $delete_clause) - { - if($delete_clause->nodeType == XML_ELEMENT_NODE) - { - switch($delete_clause->tagName) - { - case 'conditions': - checkConditionsGroup($xml_file, $delete_clause); - break; - } - } - } -} - -/** - @brief - @developer - @return - @param $xml_file - @param $query_element - */ -function validate_insert_select_query($xml_file, $query_element) -{ - foreach($query_element->childNodes as $statement_clause) - { - if($statement_clause->nodeType == XML_ELEMENT_NODE) - { - switch($statement_clause->tagName) - { - case 'query': - validate_select_query($xml_file, $statement_clause); - break; - } - } - } -} - -$validate_query_type = - array - ( - // 'insert' => - // there is currently nothing special to check - // for a plain insert, all the needed checks - // are already expressed in the .xsd - 'insert-select' => 'validate_insert_select_query', - 'update' => 'validate_update_query', - 'select' => 'validate_select_query', - 'delete' => 'validate_delete_query' - ); - -/** - @brief - @developer - @return - @param $xml_file - @param $query_element - */ -function validate_xml_query($xml_file, $query_element) -{ - global $validate_query_type; - - $action = $query_element->getAttribute('action'); - - if(array_key_exists($action, $validate_query_type)) - { - $validate_query_type[$action]($xml_file, $query_element); - } -} - -if(strpos(PHP_SAPI, 'cli') !== FALSE - || - strpos(PHP_SAPI, 'cgi') !== FALSE) -{ - /** - Saves working directory and restores it upon destruction. - Only use with single-threaded php SAPIs like CLI. - - @brief - @developer - - */ - class RestoreWorkDir - { - protected $dirname; - - /** - @brief - @developer - @return - @access - */ - public function __destruct() - { - try - { - $success = chdir($this->dirname); - } - catch (Exception $e) - { - print "Failed to restore working dir {$this->dirname}."; - } - - if(!$success) - { - print "Failed to restore working dir {$this->dirname}."; - } - } - - /** - @brief - @developer - @return - @access - */ - public function __construct() - { - $this->dirname = getcwd(); - - if(!$this->dirname) - { - throw new ErrorMessage("Failed to get current directory."); - } - } - } -} - -/** - Checks that the query_id is the same as the given file name. - For portability with case-sensitive file systems, the - actual casing of the file name from the file system is used, - and the subsequent string comparatin is case-sensitive. - - Assumes the file is known to exist (has already been opened). - - @brief - @developer - @return - @param $xml_file - @param $query_id -*/ -function validate_query_id($xml_file, $query_id) -{ - $xml_path_info = pathinfo($xml_file); - - $filename_len = strlen($xml_path_info['basename']); - $lowercase_name = strtolower($xml_path_info['basename']); - $uppercase_name = strtoupper($xml_path_info['basename']); - - if(strlen($lowercase_name) != $filename_len - || - strlen($uppercase_name) != $filename_len) - { - // multi-byte encodings may result in a different number of characters - // in the two strings - throw new ErrorMessage("Unsupported file name encoding."); - } - - // transform the given file name into a case-insensitive glob() pattern - - $varing_case_filename = ''; - - for($i = 0; $i < $filename_len; $i++) - { - if($lowercase_name[$i] != $uppercase_name[$i]) - { - $varing_case_filename .= "[{$lowercase_name[$i]}{$uppercase_name[$i]}]"; - } - else - { - $varing_case_filename .= $lowercase_name[$i]; - } - } - - $glob_pattern = $xml_path_info['dirname']; - - $restoreWorkDir = new RestoreWorkDir(); - - if($glob_pattern) - { - // change current dir to the xml file directory to keep - // glob pattern shorter (maximum 260 chars). - $success = chdir($glob_pattern); - - if(!$success) - { - throw new ErrorMessage("Failed to change work dir to {$glob_pattern}."); - } - } - - $glob_pattern = $varing_case_filename; - - // use glob() to get the file name from the file system - // realpath() would have the same effect, but it is not documented as such - $matched_files = glob($glob_pattern, GLOB_NOSORT | GLOB_NOESCAPE | GLOB_ERR); - - unset($RestoreWorkDir); // restore work dir after call to glob() - - if($matched_files === FALSE || !is_array($matched_files)) - { - throw new ErrorMessage("Directory listing for $xml_file failed."); - } - - switch(count($matched_files)) - { - case 0: - throw new ErrorMessage("Directory listing for $xml_file failed."); - - case 1: - return (pathinfo($matched_files[0], PATHINFO_FILENAME) == $query_id); - - default: - // more than one files with the same name and different case - // case-sensitive file system - foreach($mached_files as $matched_file) - { - if(pathinfo($matched_file, PATHINFO_BASENAME) == $xml_path_info['basename']) - { - return ($xml_path_info['filename'] == $query_id); - } - } - throw new ErrorMessage("Directory listing for $xml_file failed."); - - } - - throw new ErrorMessage("Internal application error."); // unreachable -} - -/** - Validate a table definition in the XML Schema Language. - Check that the size attributes is only given for FLOAT and [VAR]CHAR - types, and that it is always present for VARCHAR. - - Check that auto_increment is only given for (big)number types. - - Check for CUBRID-only/mysql+MSsql-only attributes 'auto_increment' - and 'tinytext'. - - @brief - @developer - @return - @param $xml_file - @param $table_element -*/ -function validate_schema_doc($xml_file, $table_element) -{ - foreach($table_element->childNodes as $col_node) - { - if($col_node->nodeType == XML_ELEMENT_NODE - && - $col_node->tagName == 'column') - { - $col_type = $col_node->getAttribute('type'); - $col_size = NULL; - - // check auto-increment column - if($col_node->hasAttribute('auto_increment')) - { - fwrite - ( - fopen('php://stdout', 'wt'), - $xml_file . '(' . $col_node->getLineNo() . ")\n\t" - . - ": attribute 'auto_increment' is currently supported only by SQL Server and mysql backends.\n" - ); - - static - $autoinc_types = array('number', 'bignumber'); - - if(!in_array($col_type, $autoinc_types)) - { - throw - new XmlSchemaError( - $xml_file, - $col_node->getLineNo(), - ": attribute 'auto_increment' only expected for one of the following types: " - . - implode(', ', $autoinc_types) . '.' - ); - } - } - - // check tinytext - if($col_type == 'tinytext') - { - fwrite - ( - fopen('php://stdout', 'wt'), - $xml_file . '(' . $col_node->getLineNo() . ")\n\t" - . - ": type \"tinytext\" is supported only by CUBRID.\n" - ); - } - - // check size attribute - if($col_node->hasAttribute('size')) - { - $col_size = $col_node->getAttribute('size'); - } - - if($col_type == 'varchar' && $col_size === NULL) - { - throw - new XmlSchemaError( - $xml_file, - $col_node->getLineNo(), - ": 'size' attribute expected for \"varchar\" type." - ); - } - - static - $varsize_types = array('char', 'varchar', 'float'); - - - if($col_size !== NULL && !in_array($col_type, $varsize_types)) - { - throw - new XmlSchemaError( - $xml_file, - $col_node->getLineNo(), - ": 'size' attribute only expected for the following types: " - . - implode(', ', $varsize_types) . "." - ); - } - } - } -} - -/** - Class to accumulate the highest return code when multiple files are being - processed, list return codes, and save/restore the code as needed. - - Use specific error codes depending on the validation stage and results, - so the unit tests can tell what validation step has failed. - - @brief - @developer -*/ -class ReturnCode -{ - protected $save; - protected $exit_code; - - const RETCODE_VALIDATOR_INTERNAL = 60; - const RETCODE_GENERIC_XML_SYNTAX = 50; - const RETCODE_QUERY_ELEMENT = 40; - const RETCODE_XSD_VALIDATION = 30; - const RETCODE_BUILTIN_CHECKS = 20; - const RETCODE_DB_SCHEMA_MATCH = 10; // no schema match is currently implemented. - const RETCODE_SUCCESS = 0; - - - /** - @brief - @developer - @return - @access - @param $val - */ - public function code($val = -1) - { - if($val == -1) - { - return $this->exit_code; - } - else - { - if($this->exit_code < $val) - { - $this->exit_code = $val; - } - } - } - - /** - @brief - @developer - @return - @access - @param $val - */ - public function push($val) - { - $this->save = $this->exit_code; - $this->code($val); - } - - /** - @brief - @developer - @access - @return - */ - public function pop() - { - $this->exit_code = $this->save; - $this->save = self::RETCODE_VALIDATOR_INTERNAL; - } - - /** - @brief - @developer - @return - @access - @param $val - */ - public function __construct($val = 0) - { - $this->save = self::RETCODE_VALIDATOR_INTERNAL; - $this->exit_code = $val; - } -} - -/** - @brief - @developer - */ -class UnlinkFile -{ - public $file_name; - - /** - @brief - @developer - @access - @return - */ - public function __destruct() - { - if($this->file_name) - { - unlink($this->file_name); - $this->file_name = NULL; - } - } - /** - @brief - @developer - @access - @return - @param $file_name - */ - public function __construct($file_name) - { - $this->file_name = $file_name; - } -} - -// main program entry point - -try -{ - // Explicitly set time zone, to silence some php warning about it - date_default_timezone_set('Europe/Bucharest'); - - define('CMD_NAME', basename($argv[0])); - $cmdname = CMD_NAME; - - // php manual says resources should not normally be declared constant - if(!defined('STDERR')) - { - define('STDERR', fopen('php://stderr', 'wt')); - } - - if(!defined('__DIR__')) - { - define('__DIR__', dirname(__FILE__)); - } - - - $retcode = new ReturnCode(ReturnCode::RETCODE_SUCCESS); - $auto_schema = NULL; - $schema_language = NULL; - $skip_query_id = NULL; - $xe_path = NULL; - $validate_only = NULL; - $query_args = NULL; - $query_args_file = NULL; - - while($argc >= 2 && $argv[1][0] == '-') - { - $option = $argv[1]; - - unset($argv[1]); - $argv = array_values($argv); - $argc = count($argv); - - switch($option) - { - case '-s': - case '--schema': - case '--schema-language': - if($query_args !== NULL) - { - throw new SyntaxError("Both --args-string and --schema-language options given."); - } - - if($query_args_file !== NULL) - { - throw new SyntaxError("Both --args-file and --schema-language options given."); - } - $schema_language = TRUE; - - break; - case '--auto-schema': - $auto_schema = TRUE; - - case '--skip-query-id': - $skip_query_id = TRUE; - break; - - case '--validate-only': - $validate_only = TRUE; - break; - - case '--xe-path': - case '--xe': - if($argc < 2) - { - throw - new SyntaxError("Option '{$option}' requires an argument., see `{$cmdname} --help`"); - } - - $xe_path = $argv[1]; - - unset($argv[1]); - $argv = array_values($argv); - $argc = count($argv); - - break; - - case '--arguments-string': - case '--args-string': - case '--arguments': - case '--args': - if($schema_language !== NULL) - { - throw new SyntaxError("Both --schema-language and --args-string options given."); - } - - if($query_args_file !== NULL) - { - throw new SyntaxError("Both --args-string and --args-file options given."); - } - - if($argc < 2) - { - throw - new SyntaxError("Option '{$option}' requires an argument., see `{$cmdname} --help`"); - } - - $query_args = $argv[1]; - - unset($argv[1]); - $argv = array_values($argv); - $argc = count($argv); - - break; - - case '--arguments-file': - case '--args-file': - if($schema_language !== NULL) - { - throw new SyntaxError("Both --schema-language and --args-file options given."); - } - - if($query_args !== NULL) - { - throw new SyntaxError("Both --args-string and --args-file options given."); - } - - if($argc < 2) - { - throw - new SyntaxError("Option '{$option}' requires an argument., see `{$cmdname} --help`"); - } - - $query_args_file = $argv[1]; - - unset($argv[1]); - $argv = array_values($argv); - $argc = count($argv); - - break; - - case '--help': - case '--usage': - case '/?': - case '-?': - case '-h': - case '--': - - // break out of both the switch - // and while statements - break 2; - - default: - throw - new SyntaxError("Unknown option $option, see {$cmdname} --help."); - } - } - - if($argc < 2 || - ( - $argc == 2 - && - in_array($argv[1], array('--help', '--usage', '/?', '-?', '-h')) - )) - { - throw - new SyntaxError( - "Validates an XML document against a given schema definition (XSD), using the standard php library.\n" . - "Syntax:\n" . - " {$cmdname} schema.xsd document.xml...\n" . - " {$cmdname} [ --schema-language ] [--skip-query-id] ... [--] document.xml...\n" . - "Where:\n" . - " --schema-language\n" . - " --schema\n" . - " -s\n" . - " If given, the document(s) are validated against XE XML Schema Language,\n" . - " otherwise document(s) are validated against XE XML Query Language.\n" . - "\n" . - " --skip-query-id\n" . - " Do not check the query id, which should normally match the file name.\n" . - "\n" . - " --xe-path\n" . - " --xe\n" . - " Path to XE installation. Used to load the database-specific parsers to generate\n" . - " SQL from the XML language files.\n" . - "\n" . - " --validate-only\n" . - " Only check XML schemas, no SQL generated with the database-specific parsers.\n" . - "\n" . - " --args-string \" 'name' => 'val..', 'name' => 'val...' \"\n" . - " --args-file args/file/name.php\n" . - " Variables and values for the query, if it has any (only for XML Query Language).\n" . - " Use a comma-separated 'var-name' => 'var_value...' pairs, in php syntax for an\n" . - " array constructor. The validator script will directly eval()/include() this content.\n" . - " The file named with --args-file should include an array() constructor around the\n" . - " name-value list, and should immediately return it, without a named array variable.\n" . - " E.g.:\n" . - " return \n" . - " array\n" . - " (\n" . - " 'name' => 'val',\n" . - " 'name' => 'val',\n" . - " ...\n" . - " );\n" . - "\n" . - " schema.xsd if given, is the file name for the schema definition to validate the\n" . - " document against\n" . - "\n" . - " document.xml is the file name for the XML document to be validated against the schema.\n" . - " Multiple .xml files can be given.\n" - ); - } - - $query_user_args = array(); - - // check $xe_path, $query_args - if(!$validate_only) - { - if($xe_path == NULL) - { - // assume validator.php is in directory .../xe/tools/dbxml_validator/ in an XE installation - $xe_path = dirname(dirname(realpath(__DIR__))); - } - - if(!file_exists($xe_path . '/index.php')) - { - throw - new ErrorMessage("File index.php not found in {$xe_path}."); - } - - if(!defined('_XE_PATH_')) - { - define('_XE_PATH_', $xe_path . '/'); - } - - /** - Replaces the Context class in XE. - - @brief - @developer - */ - class Context - { - protected static $db_info = NULL; - - /** - @brief - @developer - @return - @access - */ - public static function isInstalled() - { - return TRUE; - } - - /** - @brief - @developer - @return - @access - */ - public static function getLangType() - { - return 'en'; - } - - /** - @brief - @developer - @return - @access - */ - public static function getLang() - { - return 'en'; - } - - /** - @brief - @developer - @return - @access - */ - public static function getDBType() - { - if(self::$db_info) - { - return self::$db_info->master_db['db_type']; - } - else - { - return NULL; - } - } - - /** - @brief - @developer - @return - @access - @param $db_info - */ - public static function setDBInfo($db_info) - { - self::$db_info = $db_info; - } - - /** - @brief - @developer - @return - @access - */ - public static function getDBInfo() - { - return self::$db_info; - } - - /** - @brief - @developer - @return - @access - @param $str - */ - public static function convertEncodingStr($str) - { - return $str; - } - - /** - @brief - @developer - @return - @access - */ - public static function setNoDBInfo() - { - $db_info = (object)NULL; - $db_info->master_db = - array - ( - 'db_type' => NULL, - 'db_hostname' => NULL, - 'db_port' => NULL, - 'db_userid' => NULL, - 'db_password' => NULL, - 'db_database' => NULL, - 'db_table_prefix' => NULL, - 'is_connected' => TRUE // that will skip connection attempts - ); - $db_info->slave_db = array($db_info->master_db); - $db_info->use_prepared_statements = TRUE; - - self::setDBInfo($db_info); - } - - /** - @brief - @developer - @access - @return - */ - public static function setMysqlDBInfo() - { - $db_info = (object)NULL; - $db_info->master_db = - array - ( - 'db_type' => 'mysql', - 'db_hostname' => NULL, - 'db_port' => NULL, - 'db_userid' => NULL, - 'db_password' => NULL, - 'db_database' => NULL, - 'db_table_prefix' => NULL, - 'resource' => TRUE, - 'is_connected' => TRUE // that will skip connection attempts - ); - $db_info->slave_db = array($db_info->master_db); - $db_info->use_prepared_statements = TRUE; - - self::setDBInfo($db_info); - - if(array_key_exists('__DB__', $GLOBALS) - && - array_key_exists($db_info->master_db['db_type'], $GLOBALS['__DB__'])) - { - } - else - { - $GLOBALS['__DB__'][$db_info->master_db['db_type']] = - new DBMysqlConnectWrapper(); - } - - $oDB = new DB(); - $oDB->getParser(TRUE); - } - - /** - @brief - @developer - @return - @access - */ - public static function setMysqliDBInfo() - { - $db_info = (object)NULL; - $db_info->master_db = - array - ( - 'db_type' => 'mysqli', - 'db_hostname' => NULL, - 'db_port' => NULL, - 'db_userid' => NULL, - 'db_password' => NULL, - 'db_database' => NULL, - 'db_table_prefix' => NULL, - 'resource' => TRUE, - 'is_connected' => TRUE // that will skip connection attempts - ); - $db_info->slave_db = array($db_info->master_db); - $db_info->use_prepared_statements = TRUE; - - self::setDBInfo($db_info); - - if(array_key_exists('__DB__', $GLOBALS) - && - array_key_exists($db_info->master_db['db_type'], $GLOBALS['__DB__'])) - { - } - else - { - $GLOBALS['__DB__'][$db_info->master_db['db_type']] = - new DBMysqliConnectWrapper(); - } - - $oDB = new DB(); - $oDB->getParser(TRUE); - } - - /** - @brief - @developer - @return - @access - */ - public static function setCubridDBInfo() - { - $db_info = (object)NULL; - $db_info->master_db = - array - ( - 'db_type' => 'cubrid', - 'db_hostname' => NULL, - 'db_port' => NULL, - 'db_userid' => NULL, - 'db_password' => NULL, - 'db_database' => NULL, - 'db_table_prefix' => NULL, - 'resource' => TRUE, - 'is_connected' => TRUE // that will skip connection attempts - ); - $db_info->slave_db = array($db_info->master_db); - $db_info->use_prepared_statements = TRUE; - - self::setDBInfo($db_info); - - if(array_key_exists('__DB__', $GLOBALS) - && - array_key_exists($db_info->master_db['db_type'], $GLOBALS['__DB__'])) - { - } - else - { - $GLOBALS['__DB__'][$db_info->master_db['db_type']] = - new DBCubridConnectWrapper(); - } - - $oDB = new DB(); - $oDB->getParser(TRUE); - } - - /** - @brief - @developer - @return - @access - */ - public static function setMssqlDBInfo() - { - $db_info = (object)NULL; - $db_info->master_db = - array - ( - 'db_type' => 'mssql', - 'db_hostname' => NULL, - 'db_port' => NULL, - 'db_userid' => NULL, - 'db_password' => NULL, - 'db_database' => NULL, - 'db_table_prefix' => NULL, - 'resource' => TRUE, - 'is_connected' => TRUE // that will skip connection attempts - ); - $db_info->slave_db = array($db_info->master_db); - $db_info->use_prepared_statements = TRUE; - - self::setDBInfo($db_info); - - if(array_key_exists('__DB__', $GLOBALS) - && - array_key_exists($db_info->master_db['db_type'], $GLOBALS['__DB__'])) - { - } - else - { - $GLOBALS['__DB__'][$db_info->master_db['db_type']] = - new DBMssqlConnectWrapper(); - } - - $oDB = new DB(); - $oDB->getParser(TRUE); - } - } - - /** - @brief - @developer - */ - class Any_prop_obj_base - { - /** - @brief - @developer - @return - @param $property - @access - */ - public function __get($property) - { - return NULL; - } - } - - /** - @brief - @developer - */ - class LangArgFilterErrorMessage - { - /** - @brief - @developer - @return - @param $property - @access - */ - public function __get($property) - { - return 'Argument filter error'; - } - } - - global $lang; - $lang = new Any_prop_obj_base(); // to return NULL on non-existent properties - $lang->filter = New LangArgFilterErrorMessage(); - - if(!defined('__XE__')) - { - define('__XE__', TRUE); - } - - if(!defined('__ZBXE__')) - { - define('__ZBXE__', TRUE); - } - - if(!defined('__DEBUG__')) - { - define('__DEBUG__', 0); - } - - if(!defined('__DEBUG_QUERY__')) - { - define('__DEBUG_QUERY__', 0); - } - - include(_XE_PATH_ . 'classes/object/Object.class.php'); - include(_XE_PATH_ . 'classes/handler/Handler.class.php'); - include(_XE_PATH_ . 'classes/file/FileHandler.class.php'); - include(_XE_PATH_ . 'classes/page/PageHandler.class.php'); - - Context::setNoDBInfo(); - - require_once(_XE_PATH_ . 'classes/db/DB.class.php'); - require_once(_XE_PATH_ . 'classes/db/DBMysql.class.php'); - require_once(_XE_PATH_ . 'classes/db/DBMysqli.class.php'); - require_once(_XE_PATH_ . 'classes/db/DBMysql_innodb.class.php'); - require_once(_XE_PATH_ . 'classes/db/DBCubrid.class.php'); - require_once(_XE_PATH_ . 'classes/db/DBMssql.class.php'); - require_once(_XE_PATH_ . 'classes/xml/XmlParser.class.php'); - require_once(_XE_PATH_ . 'classes/xml/XmlQueryParser.class.php'); - - require_once(__DIR__ . '/connect_wrapper.php'); - - // check $query_args, $query_args_file - if($query_args_file) - { - try - { - $query_user_args = require($query_args_file); - } - catch (Exception $exc) - { - fwrite(STDERR, "Error in arguments file.\n"); - throw $exc; - } - } - else - { - if($query_args) - { - try - { - eval('$query_user_args = array(' . $query_args . ');'); - } - catch (Exception $exc) - { - fwrite(STDERR, "Error in arguments string.\n"); - throw $exc; - } - } - } - - } - - libxml_use_internal_errors(TRUE); - - $schema_file = NULL; - $schemas_set = - array - ( - 'delete' => __DIR__ . '/xml_delete.xsd', - 'update' => __DIR__ . '/xml_update.xsd', - 'select' => __DIR__ . '/xml_select.xsd', - 'insert' => __DIR__ . '/xml_insert.xsd', - 'insert-select' => __DIR__ . '/xml_insert_select.xsd' - ); - $table_schema = __DIR__ . '/xml_create_table.xsd'; - - $domDocument = new DOMDocument(); - - $i = 1; - - if(pathinfo($argv[1], PATHINFO_EXTENSION) == 'xsd') - { - $schema_file = $argv[$i++]; - } - - for(; $i < count($argv); $i++) - { - try - { - $document_schema = $schema_file; - $success = FALSE; - $use_schema_language = $schema_language; - - $retcode->push(ReturnCode::RETCODE_GENERIC_XML_SYNTAX); - if($domDocument->load($argv[$i])) - { - $retcode->pop(); - - $queryElement = $domDocument->documentElement; - - if (!$schema_language && $auto_schema) - { - if ($queryElement->tagName == 'table') - { - $use_schema_language = TRUE; - } - } - - if(!$schema_file && !$use_schema_language - && - ( - $queryElement->tagName != 'query' - || - !array_key_exists($queryElement->getAttribute('action'), $schemas_set) - )) - { - $retcode->code(ReturnCode::RETCODE_QUERY_ELEMENT); - - throw - new ErrorMessage( - "{$argv[$i]}:" . - " Root element should be and should have an action attribute of:" . - " insert, insert-select, select, update or delete." . - " Otherwise an explicit schema, to validate the document with, should be" . - " specified as first argument on the command line." - ); - } - - if(!$schema_file && !$use_schema_language && !$skip_query_id - && - !validate_query_id($argv[$i], $queryElement->getAttribute('id'))) - { - $retcode->code(ReturnCode::RETCODE_QUERY_ELEMENT); - $query_id = $queryElement->getAttribute('id'); - - throw - new ErrorMessage( - "{$argv[$i]}(" . $queryElement->getLineNo() . "):\n\tQuery 'id' attribute value \"{$query_id}\" should match file name." - ); - } - - if($use_schema_language) - { - $document_schema = $table_schema; - } - else - { - if(!$document_schema) - { - $document_schema = $schemas_set[$queryElement->getAttribute('action')]; - } - } - - $retcode->push(ReturnCode::RETCODE_XSD_VALIDATION); - if($domDocument->schemaValidate($document_schema)) - { - $retcode->pop(); - - if($use_schema_language) - { - validate_schema_doc($argv[$i], $domDocument->documentElement); - } - else - { - validate_xml_query($argv[$i], $domDocument->documentElement); - } - $success = TRUE; - } - - if(!$validate_only) - { - // Generate SQL with the db provider back-ends - - if(function_exists('sys_get_temp_dir')) - { - $tmpdir = sys_get_temp_dir(); - } - else - { - $tmpdir = getenv('TEMP'); - if(!$tmpdir) - { - $tmpdir = getenv('TMP'); - } - if(!$tmpdir) - { - $tmpdir = '/tmp'; - } - } - - - global $_SERVER; - - if(!is_array($_SERVER)) - { - $_SERVER = array(); - } - - if(!array_key_exists('REMOTE_ADDR', $_SERVER)) - { - $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - } - - $set_db_info_methods = - array - ( - 'mysql' => 'setMysqlDBInfo', - 'mysqli' => 'setMysqliDBInfo', - 'cubrid' => 'setCubridDBInfo', - 'mssql' => 'setMssqlDBInfo' - ); - - foreach($set_db_info_methods as $db_type => $set_info_method) - { - Context::$set_info_method(); // calls setMysqlDBInfo()/setCubridDBInfo()/... - - if($use_schema_language) - { - $GLOBALS['__DB__'][$db_type]->queries = ''; - $GLOBALS['__DB__'][$db_type]->createTableByXmlFile($argv[$i]); - - print "\n"; - print pathinfo($argv[$i], PATHINFO_FILENAME); - print " {$db_type} query:\n"; - print $GLOBALS['__DB__'][$db_type]->queries; - print "\n"; - } - else - { - $unlink_tmpfile = - new UnlinkFile(tempnam($tmpdir, 'xe_')); - - // copied from classes/db/DB.class.php - $oParser = new XmlQueryParser(); - $args_array = - $oParser->parse_xml_query - ( - pathinfo($argv[$i], PATHINFO_FILENAME), // query id - $argv[$i], // xml file - $unlink_tmpfile->file_name // cache file - ); - $args_array = $args_array->queryTag->getArguments(); - - $GLOBALS['__DB__'][$db_type]->queries = ''; - $k = 1; - foreach($args_array as $arg) - { - if(// why would there be a query arg without a var name ? - isset($arg->variable_name) - && - !array_key_exists($arg->variable_name, $query_user_args)) - { - if(isset($arg->argument_validator)) - { - if(FALSE // some default values are to be parsed by php, some are not... - && - isset($arg->argument_validator->default_value) - && - isset($arg->argument_validator->default_value->value)) - { - $query_user_args[$arg->variable_name] = - eval('return ' . $arg->argument_validator->default_value->toString() . ';'); - } - else - { - if($arg->argument_validator->filter) - { - switch($arg->argument_validator->filter) - { - case 'email': - case 'email_address': - $query_user_args[$arg->variable_name] = - 'user@mail.com'; - break; - - case 'homepage': - $query_user_args[$arg->variable_name] = - 'http://user.domain.srv/page_path'; - break; - - case 'userid': - case 'user_id': - $query_user_args[$arg->variable_name] = - 'user_login_name'; - break; - - case 'number': - case 'numbers': - $query_user_args[$arg->variable_name] = - 10982431; - break; - - case 'alpha': - $query_user_args[$arg->variable_name] = - 'textStringLine'; - break; - - case 'alpha_number': - $query_user_args[$arg->variable_name] = - 'textString1234Line2'; - break; - } - } - } - } - - if(!array_key_exists($arg->variable_name, $query_user_args)) - { - $query_user_args[$arg->variable_name] = sprintf('%06d', $k); - } - - if(isset($arg->argument_validator)) - { - if(isset($arg->argument_validator->min_length)) - { - $query_user_args[$arg->variable_name] = - str_pad - ( - $query_user_args[$arg->variable_name], - $arg->argument_validator->min_length, - isset($arg->argument_validator->filter) && - ( - $arg->argument_validator->filter == 'number' - || - $arg->argument_validator->filter == 'numbers' - ) - ? '0' : 'M' - ); - } - - if(isset($arg->argument_validator->max_length)) - { - $query_user_args[$arg->variable_name] = - substr - ( - $query_user_args[$arg->variable_name], - 0, - $arg->argument_validator->max_length - ); - } - } - } - - $k++; - } - - $resultset = - $GLOBALS['__DB__'][$db_type]->_executeQuery - ( - $unlink_tmpfile->file_name, // cache_file - (object)$query_user_args, // source_args - basename($argv[$i]), // query_id - array() // arg_columns - ); - - if(is_a($resultset, 'Object') && !$resultset->toBool()) - { - throw new XmlSchemaError($argv[$i], -1, 'mysql SQL query generation failed'); - } - else - { - print "\n"; - print pathinfo($argv[$i], PATHINFO_FILENAME); - print " {$db_type} query:\n"; - print $GLOBALS['__DB__'][$db_type]->queries; - print "\n"; - } - } - } - } - } - - if(!$success) - { - libXmlDisplayError($argv[$i], TRUE); - } - } - catch (XmlSchemaError $exc) - { - $retcode->code(ReturnCode::RETCODE_BUILTIN_CHECKS); - - fwrite(STDERR, $exc->getMessage() . "\n"); - } - catch (ErrorMessage $exc) - { - if($retcode->code() == ReturnCode::RETCODE_SUCCESS) - { - $retcode->code(ReturnCode::RETCODE_VALIDATOR_INTERNAL); - } - - fwrite(STDERR, $exc->getMessage() . "\n"); - libXmlDisplayError($argv[$i]); - } - catch (ErrorException $exc) - { - if($retcode->code() == ReturnCode::RETCODE_SUCCESS) - { - $retcode->code(ReturnCode::RETCODE_VALIDATOR_INTERNAL); - } - - fwrite(STDERR, "{$exc->getFile()}({$exc->getLine()}):\n\t{$exc->getMessage()}.\n"); - fwrite(STDERR, $exc->getTraceAsString()); - libXmlDisplayError($argv[$i]); - } - catch (Exception $exc) - { - $retcode->code(ReturnCode::RETCODE_VALIDATOR_INTERNAL); - - fwrite(STDERR, $exc->getMessage() . "\n"); - fwrite(STDERR, $exc->getTraceAsString()); - libXmlDisplayError($argv[$i]); - } - } - - exit($retcode->code()); -} -catch (SyntaxError $syntax) -{ - fwrite(STDERR, $syntax->getMessage() . "\n"); - exit(254); // wrong command line - // 255 is reserved by php (for parse errors, etc.) -} -catch (ErrorMessage $exc) -{ - fwrite(STDERR, $exc->getMessage() . "\n"); - libXmlDisplayError(); - exit(ReturnCode::RETCODE_VALIDATOR_INTERNAL); // internal validator error -} -catch (Exception $exc) -{ - fwrite(STDERR, $exc->getFile() . '(' . $exc->getLine() . ")\n\t" . $exc->getMessage() . "\n"); - fwrite(STDERR, $exc->getTraceAsString()); - libXmlDisplayError(); - exit(ReturnCode::RETCODE_VALIDATOR_INTERNAL); // internal validator error -} - -/* End of file validate.php */ -/* Location: tools/dbxml_validator/validate.php */ diff --git a/tools/dbxml_validator/xml_colassign.xsd b/tools/dbxml_validator/xml_colassign.xsd deleted file mode 100644 index 637e5e013..000000000 --- a/tools/dbxml_validator/xml_colassign.xsd +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Add a ColumnAssignmentType to the schema definition for SQL data-change - statements in XML Query Language in XpressEngine. - - XpressEngine is an open source framework for creating web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 08 mar 2012 - - Include graph: - - +-- xml_create_table.xsd - +-- xml_query.xsd - +-- xml_datachange.xsd - +-- xml_colassign.xsd ***** - +-- xml_insert_generic.xsd - | +-- xml_insert.xsd - | +-- xml_insert_select.xsd - | - +-- xml_select.xsd - +-- xml_update.xsd - +-- xml_delete.xsd - - The ColumnAssignamentType allows a column to be assigned a value, to be - used to describe INSERT and UPDATE statements. - - - - - - - - - - Basic type for a column assignament in an - UPDATE/INSERT. - - Maybe an expression sub-query could be allowed here as the new - column value. - - - - - - - - - - - - - - - - A column in a column assignament for an SQL UPDATE/INSERT - statement. - - "var" attribute is optional, if omitted the column name is - the variable name. - - - - - - - - - - - - - - - The list of column assignaments in an INSERT/UPDATE statement. - - - - - - - - diff --git a/tools/dbxml_validator/xml_create_table.xsd b/tools/dbxml_validator/xml_create_table.xsd deleted file mode 100644 index 1f41d0b31..000000000 --- a/tools/dbxml_validator/xml_create_table.xsd +++ /dev/null @@ -1,132 +0,0 @@ - - - - XML Schema Definition (.xsd) for the XML Schema Language for XpressEngine. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - XML Schema Language currently supports SQL CREATE TABLE statements only. - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 22 mar 2012 - - The types defined here closely model the behavior of php classes in - classes/db/DB*.class.php - - - - - - - Possible column types. Value "tinytext" is only supported by - CUBRID. - - - - - - - - - - - - - - - - - - - - Non-empty string (to be used as a name). - - - - - - - - - - - - XML Element type to represent a column definition in a - SQL CREATE TABLE statement. - - Attribute 'size' should only be used for string and numeric - column types. - - Attribute 'auto_increment' is only supported by SQL Server - and mysql. - - - - - - - - - - - - The table name "sequence" is reserved by XE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The document element, representing a SQL CREATE TABLE - statement. - - - - - - - - - diff --git a/tools/dbxml_validator/xml_datachange.xsd b/tools/dbxml_validator/xml_datachange.xsd deleted file mode 100644 index 8171bf33c..000000000 --- a/tools/dbxml_validator/xml_datachange.xsd +++ /dev/null @@ -1,100 +0,0 @@ - - - - - Add a DataChangeStatementTablesType to the main schema definition - for XML Query Language in XpressEngine. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - File: tools/xml_datachange.xsd - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 08 mar 2012 - - The components (clauses) for the SQL data-change statements (INSERT, - UPDATE, DELETE) are built upon the general query/statement components - defined in xml_query.xsd. - - Include graph: - - +-- xml_create_table.xsd - +-- xml_query.xsd - +-- xml_datachange.xsd ***** - +-- xml_colassign.xsd - +-- xml_insert_generic.xsd - | +-- xml_insert.xsd - | +-- xml_insert_select.xsd - | - +-- xml_select.xsd - +-- xml_update.xsd - +-- xml_delete.xsd - - DataChangeStatementTablesType is the type for the <tables> - element in the data-change statements. This type only allows one named - table as content, which is subject to the INSERT, UPDATE or DELETE. - - Most SQL implementations do allow multi-table UPDATE and DELETE - statements and the XML Query language can express multi-table UPDATEs. - - Also adds the priority attribute to SqlStatementType, used by the - data-change statements. - - - - - - - - - - - - - - - - - - - - - - - - - - - A named table only, no subquery/alias/join-type ... - - - - - - - - - - - - - - - - - - - A list of exactly one named table. - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_delete.xsd b/tools/dbxml_validator/xml_delete.xsd deleted file mode 100644 index 9846414bf..000000000 --- a/tools/dbxml_validator/xml_delete.xsd +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Redefines the main XML Query Language schema in XE to validate SQL DELETE - statements. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - File: tools/xml_delete.xsd - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 09 mar 2012 - - - - - - - - - - Re-define SqlStatementType for a DELETE statement: include only the - <tables>, <conditions> and <index_hint> elements. - - <conditions> could be made a required element to prevent truncating - an entire table by mistake. - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_insert.xsd b/tools/dbxml_validator/xml_insert.xsd deleted file mode 100644 index c9d6eca34..000000000 --- a/tools/dbxml_validator/xml_insert.xsd +++ /dev/null @@ -1,43 +0,0 @@ - - - - Redefines the main XML Query Language schema in XpressEngine to - validate a SQL plain INSERT statement (as opposed to an - INSERT INTO.. SELECT.. statement). - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 09 mar 2012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_insert_generic.xsd b/tools/dbxml_validator/xml_insert_generic.xsd deleted file mode 100644 index 2830a532b..000000000 --- a/tools/dbxml_validator/xml_insert_generic.xsd +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Redefines the main XML Query Language schema in XpressEngine to - validate SQL INSERT INTO.. VALUES... and INSERT INTO.. SELECT.. - statements. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 09 mar 2012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_insert_select.xsd b/tools/dbxml_validator/xml_insert_select.xsd deleted file mode 100644 index 961020129..000000000 --- a/tools/dbxml_validator/xml_insert_select.xsd +++ /dev/null @@ -1,44 +0,0 @@ - - - - Redefines the main XML Query Language schema in XpressEngine to - validate SQL INSERT.. SELECT.. statements (as opposed to a plain - INSERT INTO.. VALUES.. statement). - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 09 mar 2012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_query.xsd b/tools/dbxml_validator/xml_query.xsd deleted file mode 100644 index a9340a351..000000000 --- a/tools/dbxml_validator/xml_query.xsd +++ /dev/null @@ -1,801 +0,0 @@ - - - - - XML Schema Definition (.xsd) for the XML Query Language for XpressEngine. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 08 mar 2012 - - Strictly speaking the term "query language" is improper since the - language also describes the SQL data change statements (INSERT, UPDATE, - DELETE), not only query statements (SELECT). - - Although SQL does not distinguish between the syntax of queries and - sub-queries, the XML Query Language identifies 4 query types: - - TableSubquery: - - possibly empty (for a base table given by name) - - abstract (used as a base for the TableType) - - query navigation only (LIMIT, OFFSET) - - ExpressionSubquery - - should include at least a table - - query nagivation only - - Select-list subquery - - should include at least a table - - query nagivation only - - optional alias - - QueryStatement - - should include at least a table - - query navigation and pagination - - 'id' and 'alias' attributes - - The XML types hierarchy here shows as follows: - - +-- VarNameDefaultType - +-- SortColumnType - | - +-- MainStatementNavigationType - | +-- SimpleQueryNavigationType - | - +-- SqlStatementContentModel (abstract) - | +-- SqlStatementContents (abstract) - | | +-- SqlQueryContents (abstract) - | | | +-- SqlQueryType - | | | +-- ExpressionSubqueryContents - | | | +-- ExpressionSubqueryType - | | +-- SqlStatementType - | | - | +-- SqlTableSubquery (abstract) - | +-- TableType - | - +-- TablesType - | - +-- ColumnType - +-- ColumnsType - | - +-- ConditionType - +-- ConditionsGroupType - +-- ConditionsType - | - +-- GroupType - +-- GroupsType - | - +-- IndexHintType - +-- IndexHintsType - | - +-- pipeAttributeType - +-- dbTargetAttributeType (for db-specific index hints) - +-- HintTypeAttributeType - - +-- conditionAttrs (attribute group) - +-- argumentAttrs (query-argument attributes) - - The types defined here closely model the behavior of php classes in - classes/xml/xmlquery/**/*.php - - - - - - - Common XSD type for page-row-count/current-page/page-count elements - that are empty and only take a variable name and/or a default as - attributes. - - Note that a variable name here means an index in a php array, which - can be any string, not just an [a-zA-Z0-9_]* identifier. - - - - - - - - - - - - - - - A column name in an ORDER BY clause, given directly by name - (default attribute) or in a variable (var attribute). Either - one of the two attributes is required - - - - - - - - - - - XE treats the values "asc" and "desc" as the corresponding SQL - keywords, while all the other values are taken as a parameter - name. - - - - - - - - - - - XE sorting and paging for a main-statement query. - - Defined as a sequence of choices so that the <index> element - can appear more than once. - - - - - - - - - - - - - - - - - Sub-queries do not include a page_count - - - - - - - - - - - - - - - - - - - - The base of the SQL query and statement type hierarchy in the XML - query language. - - References all possible SQL stetement clauses, while none of them - is declared as required. It is the most generic SQL statement or - query type, though no such SQL statement instantiates it directly. - - The <index_hint> element could be allowed to appear multiple - times, in case different hints for different databases will be - allowed by the syntax. - - - - - - - - - - - - - - - - - - - - - - - - - - - An SQL generic DELETE, UPDATE, INSERT or SELECT statement. - The only common clause for these statements is the table-specification, - for which reason the <tables> element is the only one required by - the syntax - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - An SQL SELECT statement (used to form sub-queries), represented as - a case of (derived from) a generic SQL statement. - - In adition to the the table-specification required by the base - SqlStatementContents type, SQL queries also require a select-list. - This, however is implied to be "*" if missing. - So this type is the same as its base type, but is keep to represent - a SQL query, as opposed to a generic SQL statement. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Content model for <table> element. The <table> element may stand - for one of: - - a base table or view, with only a table name and an optional alias, - - a base table in a join, with the table name and join type attributes, - and the join conditions as content, - - a sub-query, with the contents of a query and the alias attribute. - - Note that the current syntax provides no means to express a joined sub-query. - Also the schema definition syntax (XSD) does not allow rules to enforce - exactly one of the above cases in a given element, but rather to only express - the sum (union) of all 3 cases for the <table> element. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - There are many more SQL statement types defined by the SQL language, but - currently the above 4 types are supported by the XML Query language in XE. - - However certain SQL CREATE statements are also supported in the XML - Schema Language in XE. - - - - - - - - - - - - - - - - - - - - - - - - - Element type for a SQL sub-query in the SELECT list or an expression. - Subqueries directly in the FROM clause are expressed by TableType. - - - - - - - - - - - - - - Base type for expression sub-queries (used in <condition>s), - which have no alias. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Columns list for a SELECT, UPDATE, INSERT statement - - - - - - - - - - - - - - - A column name for a SELECT, UPDATE, INSERT statement. - SELECT statements can have a (simple) expression as the column - name and can have an alias. - - - - - - - - - - - - - - - - For table subqueries use <table query="true">. - - - - - - - - - - - - - A <table> should include the content of a query if the table is a - subquery, that is if it also specifies the query="true" attribute. - - A <table> that specifies type="join-type..." for joining with other - tables, should include a <conditions> element with the join - condition(s). - - The particular SqlTableSubquery type is defined and needed for the base - type, instead of the general SqlQueryType, becase the contents of a - <table> element can still be empty in case of a base table, while a - subquery always has a FROM clause and a select list. - - - - - - - - - Only the query="true" value should be used. The false - value is listed here just for completeness. - - The query and name attributes should be mutually exclusive; - a sub-query should only use aliases, that is the alias - attribute, instead of name. - - - - - - - - - - - - - - - With the use of proper quoting, almost any string can be a SQL - (table) name (the exceptions are certain clases of Unicode - characters listed in the SQL standard). - - - - - - - - - - Attributes "type" and "query" should be mutually exclusive, since both - of them require the same <conditions> element in the content for - the <table>, but for different purposes. - - The type="inner join" and type="full outer join" cases are not - implemented. - - - - - - - - - - - - - - - - - - - - - - Condition expressions in a WHERE clause. Using the <group> - element is simillar to using parentheses in SQL. - - - - - - - - - - - - - - - - SQL logical operators AND and OR. - - Most database systems also support the non-standard operator XOR, - but Transact-SQL just claims the bitwise operator ^ can be used - instead, and does not provide XOR. - - - - - - - - - - - - - Attributes to describe a SQL sub-expression, common to simple subexpression - and to subexpressions with a subquery as operand - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The pipe attribute is only optional when there is one condition - in the <conditions> / <group> element. - - - - - - - - - - - - - - - - - - - - - - - - - - Certain values have special meaning: - - ipaddress() - - unixtime() - - curdate() - - plus(int count) - - minus(int count) - - multiply(int count) - - sequence() - - However these values are only special to XE, while for the schema - definition they are strings. - - - - - - - - - - - - - - - - - - - - Element type for sub-expresions with a subquery term. - - - - - - - - - - - - - - Currently only conditions of the form: - "column" op var_name - "column" op (sub-query) - are allowed by the XML query language syntax. - - The <query> element should only be provided as content if - var or default attributes are not provided. - - - - - - - - - - - - - - - - - - Groups of conditions in a WHERE clause. - Same as the global ConditionsType, plus the pipe="and|or" attribute. - - The pipes attribute is only optional in the (unlikely) case that the - <conditions> element contains only the group. - - - - - - - - - - - - - - The GROUP BY clase. - - - - - - - - - - - - A column name in the GROUP BY clause. - - - - - - - - - - - - - - Possible values for the "for" attribute on <index> hint - elements in <index_hint>. - - Specifies one of the possible databases supported by XE - (database targets) or ALL. - - Currently the 'ALL' value must be uppercase, although the database - types (mysql, mssql, cubrid) are case-insensitive. - - - - - - - - - - - - - - - Possible index hint types: IGNORE, USE or FORCE. - - Only mysql makes effective use of hints to IGNORE an index, - while mssql and CUBRID discard such hints, meaning the index - might still be used. - - - - - - - - - - - - - - List of index hints, either for a specific database, or for any database. - - - - - - - - - - - - - - Hint to use/not-use a certain index for a certain table in a query or in - a statement. - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_select.xsd b/tools/dbxml_validator/xml_select.xsd deleted file mode 100644 index b84452b95..000000000 --- a/tools/dbxml_validator/xml_select.xsd +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Redefines the main XML Query Language schema in XE to validate SQL SELECT - statements. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - File: tools/xml_select.xsd - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 08 mar 2012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xml_update.xsd b/tools/dbxml_validator/xml_update.xsd deleted file mode 100644 index 1ec50a8c6..000000000 --- a/tools/dbxml_validator/xml_update.xsd +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Redefines the main XML Query Language schema in XE to validate SQL UPDATE - statements. - - XpressEngine is an open source framework for creating your web sites. - http://xpressengine.org/ - - Author: Adrian Constantin, Arnia Software (adrian.constantin@arnia.ro) - Date: 09 mar 2012 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dbxml_validator/xsd_types.dia b/tools/dbxml_validator/xsd_types.dia deleted file mode 100644 index 7764dca5dc9fe0e2913ce092fcf6e09967576791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6512 zcmZ{pbx<74miBQ71b3GZ+(K{}+?}8a?l9P3VX#2(Ai-^LcXtnN!C`P1+=9Dq-tXSK zwcl24S9SMue&?_5s^^^QYU&6yxPJx$#-9ua*JZxekgi)@o0ZftL05txIJ8bJ$VvX9 z<8o?!k+7qfH&b|QOM!fFKAyK&`nn&SdTj-N1Rk1foYU8|KE6IVxbQFAuEw5cOAG?FOzB0$lR4Vbxr8(c(}=XY4xfT7=t8kiMSr?^Mr@WP&_w0 zPDVeW_nKpgMnqiqs#=3nhZyFb?em7Cw~EEG>hA1y_^lH;wQ~N&i_Vt6}C(IBpHB${ai>4m_rge|KsaJ3-w{4yh}A^^S0C-KdEpdSB1(KCFI8-w58k z%GF5pG4i{$P`-e5ZD};@A7^#>?RT6!6Ig?2-FL+_-aCm`X0C)AT*sX+m%X~{gxYt+ zU&Lr)@(&3 zt5y)5IuHTd8ylic&YTXD- z4qZtip6QRUt$r|TWxfJBmAru1dnU>*P{C5lLJO?>Qrl*}i0X~1M#>gX=17aS1w`$K19mo6ffE})>N%`%`dyAX zcHbzc70wZ#Dnrv;-7f*l9(})-74fVaw=r5b+~C$6sgiafRh_qD47N)&eRi7L+W=m+ z;uG2|vad5&va4g!`7x?yF1pc>P+(>H=}a+OlRU%6MCHR9heP@umrNzWH~s#?ebyIi zyf}P>$9FG03G5RXSw6@_mP&qc0<6YZ$kW~te=Sk+Ce7b=I#&6KgZxM#_0tyXKuG4~NzrKc{#UPIvOO zGIw^^$c975YFmv($e^K~oTt+9%>o=R7x=KFjzIm)HML7K4@hL^8ib?-qH#U(?35Fk zpTM4niVgb~Ju?AHjLe0KZ67S0Sx`KB@U2IsQu$`=tL^r!2`gGZM_(-P>_1p87;Yyr zFBmS&0qwsHqgzCf502spE0*l zr+m^9s~F}?P*2Yy6`GICovul(-<>=P?NZn313&DLrizi*H^@V<^>54$g#Xa1ls-DZ zJUp2~Ll-X$pfUIYW_PpFoy>CgH^A+E=P#mAEJjVjVYyt#y(wxT`a;~osU77!jm#+g z=l1n;xoK{;Lj1y+9c9LT%-^jUied*bDY7ZD$seO?_g%TCZ1+`*rmHmKL{l`nMB7ov z*vBeHQ@<$iMV~r4#+M(uIX>t1yp&ffD>EuH9?cxkL~l=B`qT{?r;TsB)yp@}4|3`< z`{0X&l5YtDa-L&?dKmJySo;i6NV&2+jk37$^km$|9%jR4Kw-EY&77p;VO?n$*g_Jn ziY9{HF8(_d(K{rO6wMUQf*UT~JN~2a$%q`N23XA$8%Tpzz5A~QZh%NS>JT5jx(kd- zTeHXyJuQ=?!xYK-W6>WJSHw}{s5R}o1#&LFu!#Ng0d*3RcwnxNC()XAI^T6BZB{1* zJk{fI*5dV^mmm-bbL75Rh7Q zi}Z*X4_v-!I@C+KDGj%V;Tzxdi&)N0c4wDiV|vUJy#lF-Z@D;4jJn(X!SI&|yU1f0 zDa8K)ro-)?fybp#Sr2VA8H9%(M@MY|calUs%V@tLtO?LYMzrqbzTuO!t=#qc@FHyOxd5gb=9rZi>++u}n zsr_1Th)SV@bJ9@o<%ECnVnvaZCwnU5?h~-%Fpfa2cDY%;l!?5bzfE<0OMtu1B#VI4 zef;ThT*V&az9}8Jv+EIwI%{UDAw`#Q)TTvQ;(hCQU+SUTBCbipY|X$tNK&_F^Mo=m zP^BiaRCKj(n3i_m3Bb*a3~TRC}#Egn^MCVe%z2^nCuuD>V4gFqC@{u~9N_w@TC_rZJz z(CXT#v-F$DS~?P{=Ufc-jPI2=(*tHOiilM?(~Sf=oV{RIJqN-625ZS%SF*NviLck| z$@aJNubtvwQRbC}=f@85QPNW{-K>gd6#G9m42_BjSwh=47U@(CPn7+7DS8%Lpu)=( z?bc){ee3Jf4r?cOV;nlWrj~2r5Z)dWOQVJ75fR0xRkGx2fa{xv&%4T$#&V?2p=cbh zlq96gkE>+8j=Z%%ksbuosoYp!J>N--KNBm|r*34%M%vIz7^9Y;RN}`w-ZS6^w7V+6 z7_ke*5A%uaAHZQ7kyKUBeCN)cQBX4gt9~zVV=wqD`bFqk?z&Tff9ZXc5@?w3Ktu`f zyUn<7z%)K>IiG-aVccB9{wZdzotfD7H$)+J!uowa@-%`i{CChi8VV5cm-BTR^XQXI z@%LiP=)X2v|32xuZtv*3;L*4#eO#4s%ng!PLd+sML*9tM59O3Zy!$>~C2o91G(Xbn zd$O>zVS&TpM$n*>^ytgczi^?cpJ&8tz)D{G(U_`j*vKS@jc5d+9nKT}3}FMg8^fO% zjuXxZ?k_lZAsQJOyaLsh--Bx$F?EI;H>Ogm7)8f&K^&90AE>1Sc$O*1ng~Nf4uUmT zZs-=Baf>``xB1hl+DaCOn|UBox5~XScaKY8%rB|*uBKDnxq_ewt5qw(u{@aZuRPoV zD$MhSB+p81+9Xpg0$i65+3C%h>VPPGmo{ycC}HSi`MJo{6kiet=nMGJ#}UBqsTg36 zIEWIBNkJ~aM|`}{C;5!}pU~NUW1(GSa2KYY++HvHetPMIfhFDSQjl$RS-B$r(l*2w zL`#Q*8#WY6$F20aybNT9m;2yxK)xxdl>MF)l@Y9)bkC%3t*|it>#DLd^HfCj$5Kp( z9fXa^>5`LQ7<+wQKYVs_&E9oBQdoU^q`8LgVg~&AeVqmU+=5wx-0=F_+J>IqUqu2e z=+{LR_~jEl)$w&)$D3?#9WsM+D(WVE3V=3=IU7OV5e1dAy1ave?=~Ybu$&+Q zB;}_u;%@~`U3(tl&0i#X=yLvj}E@4g3(?U$g~oTO&A*lR1T zN(H6-(E)i|IGU7qQN4MfI=?%|Z;M6pMvuD=Q%B%-Mh4}ltK%W*L=>ldt$dS8j?;Rm z=^9~Cxo;u{5!0g8Ptbr*z+fU)=g2nrA5y%<-^Ca!q3qmW>AlJ9DsDEhI49hG`GMR# zL!Q_7w~o(FVQA7wbY?2jR~|;{xW#B%NGvY(KeTR>M=xH5)oP)HBz`t%Bri&wZ%ETN zu`1<6ri%D9PziI@T9#^*)bY?mZ%Sy^?k}t$=8Xj^@*-V{vUXz>yjC3k`X*M%;i>lw zDWr9wsecGfc1bX*_fhHVl_4#aJ=%zyHcQGZ=rb(GI4~jW_Bhk!1qWL~oE)U9>OwPQ z{RNg$d*COVkM;MtrI#qe&`&%#d)Jf%8CWzXq_gS?$??nYqJM2W=8kdW5xUbOD`1L% zoeoNP(Jm?{7~ZTXVv{Ex5cY|BeOn7bIwKq1XcB|G;%ZNMgF$MWM~Z;a5+3Ai6Y*dE z-{FxF(fw-Foq4EGYfza;)VcnHmxI3Fl5hX5_m~&dMRkWSZ<{zGr@{QOtvqFrhtrUG zP;zE-!86jAvp@-`F}k5zKpvbN{4`2FCK&=|KnPsrtDUpx->dxY?uUardHv61l!Tu4 zh3aA4sYltn_WY@p3MlU7`fwJME4cHODyN5A$YP;Qb_Iu#u3jGV!)6u3e&9_>&XL-| zI}RjGw0sSUU{NNK_0~1epuw+NtnHOM11)ffA!Kw$v zRxqna^3c z>wo&&?zF8eNpxpTn|EVLFFOFPJ8Bi^X`oZG64G~>PEFCKj{`l}8l+tlGxt2+MVzZ> z^xPCE0&ks3Hn_S?{d?g;;js|^1=MSC-eYLgp5e74&SFS*(1<_z-%S)7O){y-ej7#| z1?w1=CW%XA)2QQUQAqWF`0K6h+Ic(r2LqF$)+^e^^-08iS@da4u1ak*| z)05(-H}z#M|FkcMx59jx~wj$mE>gNnyzZfFIO!UPsG)-(U_r!1Y*C}(FE;x4Lc5Gnl&jc1a z#7!N3SCDU;AUsAR(^3;#>&4bwI;rbmRd&%67NG@Jk-Mh)#?IOD8|XM%c5pFg_GgY< zs^h%yGhFL;qLJ49D3Vk;$u>*FE53McWg3)B*6VPv?}@yAqrrTC_17qW4n@g<6-L=$ znN}qS+r<#(TIyTL7Gj#u;`RPE@KXr6$kG^LL^$mK0v$t9jAC-g6sC9lXHFYv)DwvW z@%KCttPL%|a2CQz9Gfr|OnsFw6JIdu(P%Hxe6s5wd?e`wL*vTZsu)v)56&G+D2ZZ( zjnHKBn@ZS&i(Gw-pPhxAo-_5;V1B#6*Kf)_?*2z`Q20tjgHQ|)O0%4oz<-CR%~;Nq zMwv%6V$3_wQw4?SM{CTD$Ak6&nN6Hm z0opq}VjrMpl&O-37q`7{51M|ytm0+hmNBpQcbpkG6ySj-{A#Fr=}BR$xZ*xJ-D~C+{AHH|*~n%QgdIk#5=Cqu zvl|VRfEv>Mkwnn0R4w?*cJ6{S*FTvylXzm`!e6@KLOyaBHToWB+V0`X9Rv3@ZnHyj zWDJ4Dh7POIRM|jTHNj$ItM(r8Xw!6N_QT?s#m~&#r!>Jv@n2hYbCwOiT+y;HoS#nZ zSJ~h>>6`VXmV`X2`a-vh$p_784`jJdfDDiuV^04D2^^fmhjumX<4;-_R--AONq@<* zruyKu0tm+vA1Lntdqle2!QAucT0?FA*Cpga30?^q#t=!hs;^kQj>5~!AWy~({wyS%5oR;X;;6!>5f*>J0u`aO|Z@4&+c z9u&1|P#QV=a;MfH`r>%M>2W$j%Nj#CG_uqnz#3WA?- z1}vny(RFxg6u^oQU`3FRE%Lo)@mx`P6>-B-qI}MLRjGVH@%uQurqi;1bdMaEPOB%v zWVO-)%UL%ZCL$RM&s!UkNv>>D@m~KB`2P>+`B%eXn*TjKyi>aypKX$?g3cbh1g<|F zoQ!iL!7tqR!#Bi9SIR2f+A!I~4BWmt`?z8G&eDRdd5=dL0%@ihWoVeFN_4iq?{J1x zb3uuk$W-~k^e6iTEqmY(^j0RGMU|XkI-S=gU&eW_5`}SSsmz6xoytXnWa8gfHN_d= zX4EN%DlO0O8^c_mBQKAfIc9=qDM4CQCoJ-;u9C)9$SunGjhfQ=maa{M-?kmwnej%~ z(stKwT}^xcpY&JsNXt09!_t=VaO+R+nL#~5kvM_kYjZ3Cumpjg(m2xF%Gj6^$um}+ zFd5p*IZ7uCta&Q*V>MsVKmOEW zreuaoJY+kI0#iRODb|8~zPdszw2NjAhzW13Wrj@tCg9~|JG3_j zyMJns-~!1!P)h^izPA3=9ZoA)*MXg@pGju zx<F3JxF5%y5oi!OLo zUz;5Lnh&f%{es>2-+(is0!qR4L*jb}!U!5S$;_LX1EivvcZ6^0NM_KaxfBVS#9+WW zbOGu~@@#(V=(QjsJA$^^S?wDnjeV&s^s?X;EfU>e4Z;anq9@|pb2YXXq)((Kc-AkvQc7j}90!LkdW^199>qcxhZoRQngI!%h>Jq2HkbLG6HZ%uu2M&0t zAH#Jvky5-O81%)!N!DPuMk=iorvG6m>wFpt>7;4B@O;E{-Vy9cOs_hReUWf`&tJ%T hbUL#S;Q0Gn@gn;o7E5jM_p{?wB7cAkZw~?PzW|7xr#Aoq diff --git a/tools/phpDoc/phpdoc.dist.xml b/tools/phpDoc/phpdoc.dist.xml deleted file mode 100644 index 945d2d4b5..000000000 --- a/tools/phpDoc/phpdoc.dist.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - <![CDATA[Rhymix]]> - - rhymix - . - - - output - - -