Added unit tests for correlated subqueries - select, from, where.

git-svn-id: http://xe-core.googlecode.com/svn/branches/1.5.0-DB@8556 201d5d3c-b55e-5fd7-737f-ddc643e51545
This commit is contained in:
ucorina 2011-06-30 15:36:03 +00:00
parent 5d1eb1c21e
commit 1353ade0c2
41 changed files with 661 additions and 256 deletions

View file

@ -319,6 +319,7 @@
require_once(_XE_PATH_.'classes/db/queryparts/order/OrderByColumn.class.php');
require_once(_XE_PATH_.'classes/db/queryparts/limit/Limit.class.php');
require_once(_XE_PATH_.'classes/db/queryparts/Query.class.php');
require_once(_XE_PATH_.'classes/db/queryparts/Subquery.class.php');
$output = include($cache_file);

View file

@ -199,7 +199,7 @@
/**
* @brief Fetch results
**/
function _fetch($result) {
function _fetch($result, $arrayIndexEndValue = NULL) {
if(!$this->isConnected() || $this->isError() || !$result) return;
$c = sqlsrv_num_fields($result);
@ -212,10 +212,14 @@
for($i=0;$i<$c;$i++){
$row->{$m[$i]['Name']} = sqlsrv_get_field( $result, $i, SQLSRV_PHPTYPE_STRING( 'utf-8' ));
}
$output[] = $row;
if($arrayIndexEndValue) $output[$arrayIndexEndValue--] = $row;
else $output[] = $row;
}
if(count($output)==1) return $output[0];
if(count($output)==1) {
if(isset($arrayIndexEndValue)) return $output;
else return $output[0];
}
return $output;
}

View file

@ -135,8 +135,7 @@
foreach($this->columns as $column){
if($column->show())
if(is_a($column, 'Subquery')){
$oDB = &DB::getInstance();
$select .= '(' .$oDB->getSelectSql($column, $with_values) . ') as \''. $column->getAlias().'\', ';
$select .= $column->toString($with_values) . ' as '. $column->getAlias() .', ';
}
else
$select .= $column->getExpression($with_values) . ', ';
@ -169,12 +168,18 @@
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
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(!$table->isJoinTable()) $from .= $table->getAlias() ? ' as ' . $table->getAlias() . ' ' : ' ';
$simple_table_count++;
}
if(trim($from) == '') return '';

View file

@ -2,12 +2,13 @@
class Subquery extends Query {
var $alias;
function Subquery($alias, $columns, $tables, $conditions, $groups, $orderby, $limit){
var $join_type;
function Subquery($alias, $columns, $tables, $conditions, $groups, $orderby, $limit, $join_type = null){
$this->alias = $alias;
$this->queryID = null;
$this->action = null;
$this->action = "select";
$this->columns = $columns;
$this->tables = $tables;
@ -15,11 +16,23 @@
$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) . ')';
}
}
?>

View file

@ -15,6 +15,8 @@
$this->pipe = $pipe;
if($this->hasArgument())
$this->_value = $argument->getValue();
else if(is_a($this->argument, 'Subquery'))
$this->_value = $argument->toString();
else
$this->_value = $argument;
}
@ -50,27 +52,27 @@
function show(){
switch($this->operation) {
case 'equal' :
case 'more' :
case 'excess' :
case 'less' :
case 'below' :
case 'like_tail' :
case 'like_prefix' :
case 'like' :
case 'in' :
case 'notin' :
case 'notequal' :
// if variable is not set or is not string or number, return
if(!isset($this->_value)) return false;
if($this->_value === '') return false;
if(!in_array(gettype($this->_value), array('string', 'integer'))) return false;
break;
case 'between' :
if(!is_array($this->_value)) return false;
if(count($this->_value)!=2) return false;
case 'equal' :
case 'more' :
case 'excess' :
case 'less' :
case 'below' :
case 'like_tail' :
case 'like_prefix' :
case 'like' :
case 'in' :
case 'notin' :
case 'notequal' :
// if variable is not set or is not string or number, return
if(!isset($this->_value)) return false;
if($this->_value === '') return false;
if(!in_array(gettype($this->_value), array('string', 'integer'))) return false;
break;
case 'between' :
if(!is_array($this->_value)) return false;
if(count($this->_value)!=2) return false;
}
}
return true;
}
@ -78,43 +80,43 @@
$name = $this->column_name;
$operation = $this->operation;
switch($operation) {
case 'equal' :
return $name.' = '.$value;
break;
case 'more' :
return $name.' >= '.$value;
break;
case 'excess' :
return $name.' > '.$value;
break;
case 'less' :
return $name.' <= '.$value;
break;
case 'below' :
return $name.' < '.$value;
break;
case 'like_tail' :
case 'like_prefix' :
case 'like' :
return $name.' like '.$value;
break;
case 'in' :
return $name.' in ('.$value.')';
break;
case 'notin' :
return $name.' not in ('.$value.')';
break;
case 'notequal' :
return $name.' <> '.$value;
break;
case 'notnull' :
return $name.' is not null';
break;
case 'null' :
return $name.' is null';
break;
case 'between' :
switch($operation) {
case 'equal' :
return $name.' = '.$value;
break;
case 'more' :
return $name.' >= '.$value;
break;
case 'excess' :
return $name.' > '.$value;
break;
case 'less' :
return $name.' <= '.$value;
break;
case 'below' :
return $name.' < '.$value;
break;
case 'like_tail' :
case 'like_prefix' :
case 'like' :
return $name.' like '.$value;
break;
case 'in' :
return $name.' in ('.$value.')';
break;
case 'notin' :
return $name.' not in ('.$value.')';
break;
case 'notequal' :
return $name.' <> '.$value;
break;
case 'notnull' :
return $name.' is not null';
break;
case 'null' :
return $name.' is null';
break;
case 'between' :
return $name.' between ' . $value[0] . ' and ' . $value[1];
break;
}

View file

@ -10,7 +10,8 @@
}
function toString(){
return sprintf("%s%s", $this->name, $this->alias ? ' as ' . $this->alias : '');
return $this->name;
//return sprintf("%s%s", $this->name, $this->alias ? ' as ' . $this->alias : '');
}
function getName(){

View file

@ -13,13 +13,21 @@
require_once(_XE_PATH_.'classes/xml/xmlquery/QueryParser.class.php');
class XmlQueryParser extends XmlParser {
var $dbParser;
static $dbParser = null;
var $db_type;
function XmlQueryParser($db_type = NULL){
$this->db_type = $db_type;
}
function &getInstance($db_type = NULL){
static $theInstance = null;
if(!isset($theInstance)){
$theInstance = new XmlQueryParser($db_type);
}
return $theInstance;
}
function parse($query_id, $xml_file, $cache_file) {
// Read xml file
@ -36,15 +44,19 @@
// singleton
function &getDBParser(){
static $dbParser;
if(!$dbParser){
if(isset($this->db_type))
$oDB = &DB::getInstance($this->db_type);
if(!$self->dbParser){
is_a($this,'XmlQueryParser')?$self=&$this:$self=&XmlQueryParser::getInstance();
if(isset($self->db_type))
$oDB = &DB::getInstance($self->db_type);
else
$oDB = &DB::getInstance();
$dbParser = $oDB->getParser();
$self->dbParser = $oDB->getParser();
}
return $dbParser;
return $self->dbParser;
}
function setDBParser($value){
$self->dbParser = $value;
}
function getXmlFileContent($xml_file){

View file

@ -20,8 +20,7 @@
if(!is_array($xml_columns)) $xml_columns = array($xml_columns);
foreach($xml_columns as $column){
if($column->node_name == 'query') $this->columns[] = new QueryTag($column, true);
else $this->columns[] = new SelectColumnTag($column);
$this->columns[] = new SelectColumnTag($column);
}

View file

@ -11,8 +11,8 @@
if(count($conditions))require_once(_XE_PATH_.'classes/xml/xmlquery/tags/condition/ConditionTag.class.php');
foreach($conditions as $condition){
if($condition->name === 'query') $this->conditions[] = new QueryTag($condition, true);
else $this->conditions[] = new ConditionTag($condition);
//if($condition->node_name === 'query') $this->conditions[] = new QueryTag($condition, true);
$this->conditions[] = new ConditionTag($condition);
}
}
@ -22,8 +22,9 @@
function getConditionGroupString(){
$conditions_string = 'array('.PHP_EOL;
foreach($this->conditions as $condition)
foreach($this->conditions as $condition){
$conditions_string .= $condition->getConditionString() . PHP_EOL . ',';
}
$conditions_string = substr($conditions_string, 0, -2);//remove ','
$conditions_string .= ')';
@ -33,6 +34,9 @@
function getArguments(){
$arguments = array();
foreach($this->conditions as $condition){
if(is_a($condition, 'QueryTag'))
$arguments = array_merge($arguments, $condition->getArguments());
else
$arguments[] = $condition->getArgument();
}
return $arguments;

View file

@ -16,6 +16,7 @@
var $argument;
var $default_column;
var $query;
function ConditionTag($condition){
$this->operation = $condition->attrs->operation;
$this->pipe = $condition->attrs->pipe;
@ -23,20 +24,20 @@
$this->column_name = $dbParser->parseColumnName($condition->attrs->column);
$isColumnName = strpos($condition->attrs->default, '.');
if($condition->attrs->var || $isColumnName === false){
if($condition->node_name == 'query'){
$this->query = new QueryTag($condition, true);
$this->default_column = $this->query->toString();
}
else if($condition->attrs->var || $isColumnName === false){
require_once(_XE_PATH_.'classes/xml/xmlquery/queryargument/QueryArgument.class.php');
//$this->argument_name = $condition->attrs->var;
$this->argument = new QueryArgument($condition);
$this->argument_name = $this->argument->getArgumentName();
}
//else if($isColumnName === false){
// $this->default_column = $condition->attrs->default;
//}
else {
$this->default_column = $dbParser->parseColumnName($condition->attrs->default);
}
$this->default_column = "'" . $dbParser->parseColumnName($condition->attrs->default) . "'" ;
}
}
function setPipe($pipe){
@ -49,11 +50,11 @@
function getConditionString(){
return sprintf("new Condition('%s',%s,%s%s)"
, $this->column_name
, $this->default_column ? "'" . $this->default_column . "'" : '$' . $this->argument_name . '_argument'
, '"'.$this->operation.'"'
, $this->pipe ? ", '" . $this->pipe . "'" : ''
);
, $this->column_name
, $this->default_column ? $this->default_column: '$' . $this->argument_name . '_argument'
, '"'.$this->operation.'"'
, $this->pipe ? ", '" . $this->pipe . "'" : ''
);
}
}
?>

View file

@ -5,7 +5,15 @@
function ConditionsTag($xml_conditions){
$this->condition_groups = array();
$xml_condition_list = $xml_conditions->condition;
$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){
require_once(_XE_PATH_.'classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php');
$this->condition_groups[] = new ConditionGroupTag($xml_condition_list);

View file

@ -17,6 +17,7 @@ class QueryTag {
var $buff;
var $isSubQuery;
var $join_type;
var $alias;
function QueryTag($query, $isSubQuery = false){
@ -26,6 +27,7 @@ class QueryTag {
$this->isSubQuery = $isSubQuery;
if($this->isSubQuery) $this->action = 'select';
$this->alias = $query->attrs->alias;
$this->join_type = $query->attrs->join_type;
$this->getColumns();
$tables = $this->getTables();
@ -55,8 +57,10 @@ class QueryTag {
$table_tags = $tables->getTables();
$column_type = array();
foreach($table_tags as $table_tag){
if(is_a($table_tag, 'TableTag')){
$tag_column_type = QueryParser::getTableInfo($query_id, $table_tag->getTableName());
$column_type = array_merge($column_type, $tag_column_type);
}
}
$this->column_type[$query_id] = $column_type;
}
@ -94,17 +98,17 @@ class QueryTag {
function getBuff(){
$buff = '';
//echo 'Luam un query care e '.$this->isSubQuery;
if($this->isSubQuery){
$buff = 'new Subquery(';
$buff .= "'" . $this->alias . '\', ';
$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 .= $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;
@ -129,7 +133,7 @@ class QueryTag {
}
function getTables(){
return $this->tables = new TablesTag($this->query->tables->table);
return $this->tables = new TablesTag($this->query->tables);
}
function getConditions(){
@ -160,6 +164,7 @@ class QueryTag {
return $this->buff;
}
function getArguments(){
$arguments = array();
if($this->columns)
@ -170,4 +175,4 @@ class QueryTag {
}
}
?>
?>

View file

@ -6,6 +6,17 @@
* @author Arnia Sowftare
* @brief Models the <table> tag inside an XML Query file
*
* @abstract
* Example
* <table name="modules" />
* <table name="documents" alias="doc" />
* 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 <conditions>
*/
class TableTag {
@ -16,7 +27,6 @@
var $conditions;
function TableTag($table){
$this->unescaped_name = $table->attrs->name;
$dbParser = XmlQueryParser::getDBParser();

View file

@ -3,16 +3,29 @@
class TablesTag {
var $tables;
function TablesTag($xml_tables){
$this->tables = array();
if(!is_array($xml_tables)) $xml_tables = array($xml_tables);
if(count($xml_tables)) require_once(_XE_PATH_.'classes/xml/xmlquery/tags/table/TableTag.class.php');
foreach($xml_tables as $table){
if($table->name === 'query') $this->tables[] = new QueryTag($table, true);
else $this->tables[] = new TableTag($table);
}
function TablesTag($xml_tables_tag){
$xml_tables = $xml_tables_tag->table;
$xml_queries = $xml_tables_tag->query;
$this->tables = array();
if($xml_tables){
if(!is_array($xml_tables)) $xml_tables = array($xml_tables);
if(count($xml_tables)) require_once(_XE_PATH_.'classes/xml/xmlquery/tags/table/TableTag.class.php');
foreach($xml_tables as $table){
if($table->name === 'query') $this->tables[] = new QueryTag($table, true);
else $this->tables[] = new TableTag($table);
}
}
if(!$xml_queries) return;
if(!is_array($xml_queries)) $xml_queries = array($xml_queries);
foreach($xml_queries as $table){
$this->tables[] = new QueryTag($table, true);
}
}
function getTables(){
@ -21,8 +34,11 @@
function toString(){
$output_tables = 'array(' . PHP_EOL;
foreach($this->tables as $table){
$output_tables .= $table->getTableString() . 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 .= ')';