diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 5af8aca2a..8b0e70682 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -239,7 +239,12 @@ class Context { $oInstallController = &getController('install'); $oInstallController->makeConfigFile(); } - + + if(!$db_info->use_prepared_statements) + { + $db_info->use_prepared_statements = 'Y'; + } + if(!$db_info->time_zone) $db_info->time_zone = date('O'); $GLOBALS['_time_zone'] = $db_info->time_zone; diff --git a/classes/db/DB.class.php b/classes/db/DB.class.php index 85339e092..48dd0e997 100644 --- a/classes/db/DB.class.php +++ b/classes/db/DB.class.php @@ -82,6 +82,8 @@ var $db_type; ///< stores database type: 'mysql','cubrid','mssql' etc. or 'db' when database is not yet set + var $use_prepared_statements; ///< flag to decide if class prepared statements or not (when supported); can be changed from db.config.info + /** * @brief returns instance of certain db type * @param[in] $db_type type of db @@ -762,6 +764,7 @@ else $this->slave_db = $db_info->slave_db; $this->prefix = $db_info->master_db["db_table_prefix"]; + $this->use_prepared_statements = $db_info->use_prepared_statements; } function __connect(){ diff --git a/classes/db/DBCubrid.class.php b/classes/db/DBCubrid.class.php index 43a384b00..c3025f91d 100644 --- a/classes/db/DBCubrid.class.php +++ b/classes/db/DBCubrid.class.php @@ -169,6 +169,55 @@ **/ function __query($query, $connection) { + if($this->use_prepared_statements == 'Y') + { + $req = @cubrid_prepare($connection, $query); + $position = 0; + if($this->param) + { + foreach($this->param as $param) + { + $value = $param->getUnescapedValue(); + $type = $param->getType(); + + if($param->isColumnName()) continue; + + switch($type) + { + case 'number' : + $bind_type = 'numeric'; + break; + case 'varchar' : + $bind_type = 'string'; + break; + default: + $bind_type = 'string'; + } + + if(is_array($value)){ + foreach($value as $v) + { + cubrid_bind($req, ++$position, $v, $bind_type); + } + } + else + { + cubrid_bind($req, ++$position, $value, $bind_type); + } + } + } + + $result = @cubrid_execute($req); + if(!$result) + { + $code = cubrid_error_code (); + $msg = cubrid_error_msg (); + + $this->setError ($code, $msg); + } + return $req; + + } // Execute the query $result = @cubrid_execute ($connection, $query); // error check @@ -190,6 +239,11 @@ $output = array(); if (!$this->isConnected() || $this->isError() || !$result) return array(); + if($this->use_prepared_statements == 'Y') + { + + } + // TODO Improve this piece of code // This code trims values from char type columns $col_types = cubrid_column_types ($result); @@ -547,9 +601,14 @@ /** * @brief handles insertAct **/ - function _executeInsertAct($queryObject) + function _executeInsertAct($queryObject, $with_values = true) { - $query = $this->getInsertSql($queryObject); + if($this->use_prepared_statements == 'Y') + { + $this->param = $queryObject->getArguments(); + $with_values = false; + } + $query = $this->getInsertSql($queryObject, $with_values); if(is_a($query, 'Object')) return; $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; @@ -558,22 +617,27 @@ if ($result && !$this->transaction_started) { $this->_commit(); } - + unset($this->param); return $result; } /** * @brief handles updateAct **/ - function _executeUpdateAct($queryObject) + function _executeUpdateAct($queryObject, $with_values = true) { - $query = $this->getUpdateSql($queryObject); + if($this->use_prepared_statements == 'Y') + { + $this->param = $queryObject->getArguments(); + $with_values = false; + } + $query = $this->getUpdateSql($queryObject, $with_values); if(is_a($query, 'Object')) return; $result = $this->_query($query); if ($result && !$this->transaction_started) $this->_commit(); - + unset($this->param); return $result; } @@ -581,15 +645,21 @@ /** * @brief handles deleteAct **/ - function _executeDeleteAct($queryObject) + function _executeDeleteAct($queryObject, $with_values = true) { - $query = $this->getDeleteSql($queryObject); + if($this->use_prepared_statements == 'Y') + { + $this->param = $queryObject->getArguments(); + $with_values = false; + } + $query = $this->getDeleteSql($queryObject, $with_values); if(is_a($query, 'Object')) return; $result = $this->_query ($query); if ($result && !$this->transaction_started) $this->_commit(); - + + unset($this->param); return $result; } @@ -599,25 +669,32 @@ * to get a specific page list easily in select statement,\n * a method, navigation, is used **/ - function _executeSelectAct($queryObject, $connection = null){ - $limit = $queryObject->getLimit(); - if ($limit && $limit->isPageHandler()) - return $this->queryPageLimit($queryObject, $result, $connection); - else { - $query = $this->getSelectSql($queryObject); - if(is_a($query, 'Object')) return; + function _executeSelectAct($queryObject, $connection = null, $with_values = true) { + if ($this->use_prepared_statements == 'Y') { + $this->param = $queryObject->getArguments(); + $with_values = false; + } + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) + return $this->queryPageLimit($queryObject, $connection, $with_values); + else { + $query = $this->getSelectSql($queryObject, $with_values); + if (is_a($query, 'Object')) + return; - $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; - $result = $this->_query ($query, $connection); + $query .= (__DEBUG_QUERY__ & 1 && $queryObject->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; + $result = $this->_query($query, $connection); - if ($this->isError ()) - return $this->queryError($queryObject); + if ($this->isError()) + return $this->queryError($queryObject); - $data = $this->_fetch($result); - $buff = new Object (); - $buff->data = $data; - return $buff; - } + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + + unset($this->param); + return $buff; + } } function queryError($queryObject){ @@ -634,11 +711,11 @@ return; } - function queryPageLimit($queryObject, $result, $connection){ - $limit = $queryObject->getLimit(); + function queryPageLimit($queryObject, $connection, $with_values){ + $limit = $queryObject->getLimit(); // Total count - $temp_where = $queryObject->getWhereString(true, false); - $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + $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)); if ($queryObject->getGroupByString() != '') { $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); } @@ -677,11 +754,11 @@ } $start_count = ($page - 1) * $list_count; - $query = $this->getSelectPageSql($queryObject, true, $start_count, $list_count); - $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $query = $this->getSelectPageSql($queryObject, $with_values, $start_count, $list_count); + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; $result = $this->_query ($query, $connection); - if ($this->isError ()) - return $this->queryError($queryObject); + if ($this->isError ()) + return $this->queryError($queryObject); $virtual_no = $total_count - ($page - 1) * $list_count; $data = $this->_fetch($result, $virtual_no); @@ -692,11 +769,12 @@ $buff->page = $page; $buff->data = $data; $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); + unset($this->param); return $buff; } function getParser(){ - return new DBParser('"', '"', $this->prefix); + return new DBParser('"', '"', $this->prefix); } function getSelectPageSql($query, $with_values = true, $start_count = 0, $list_count = 0) { diff --git a/classes/db/DBMssql.class.php b/classes/db/DBMssql.class.php index 43b467306..63edb8af8 100644 --- a/classes/db/DBMssql.class.php +++ b/classes/db/DBMssql.class.php @@ -135,36 +135,76 @@ if(count($this->param)){ foreach($this->param as $k => $o){ - if($o->getType() == 'number'){ - $value = $o->getUnescapedValue(); - if(is_array($value)) $_param = array_merge($_param, $value); - else $_param[] = $o->getUnescapedValue(); - }else{ - $value = $o->getUnescapedValue(); - if(is_array($value)) { - foreach($value as $v) - $_param[] = array($v, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); - } - else $_param[] = array($value, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); - } + if($o->isColumnName()) continue; + if($o->getType() == 'number'){ + $value = $o->getUnescapedValue(); + if(is_array($value)) $_param = array_merge($_param, $value); + else $_param[] = $o->getUnescapedValue(); + }else{ + $value = $o->getUnescapedValue(); + if(is_array($value)) { + foreach($value as $v) + $_param[] = array($v, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); + } + else $_param[] = array($value, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); + } } } // Run the query statement $result = false; - if(count($_param)){ - $result = @sqlsrv_query($connection, $query, $_param); + if(count($_param)){ + $args = $this->_getParametersByReference($_param); + $stmt = sqlsrv_prepare($connection, $query, $args); }else{ - $result = @sqlsrv_query($connection, $query); + $stmt = sqlsrv_prepare($connection, $query); } + + if(!$stmt) + { + $result = false; + } + else + { + $result = sqlsrv_execute($stmt); + } + // Error Check - - if(!$result) $this->setError(print_r(sqlsrv_errors(),true)); + if(!$result) + $this->setError(print_r(sqlsrv_errors(),true)); $this->param = array(); - return $result; + return $stmt; } + + /** + * Parameters to sqlsrv_prepare need to be references, and not literals + * Parameters are sent as an array, where each parameter can be: + * - a PHP variable (by reference) + * - a PHP array (containng param value, type and direction) -> also needs to be sent by reference + */ + function _getParametersByReference($_param) + { + $copy = array(); $args = array(); $i = 0; + foreach($_param as $key => $value) { + if(is_array($value)) + { + $value_copy = $value; + $value_arg = array(); + $value_arg[] = &$value_copy[0]; + $value_arg[] = $value_copy[1]; + $value_arg[] = $value_copy[2]; + } + else + { + $value_arg = $value; + } + $copy[$key] = $value_arg; + $args[$i++] = &$copy[$key]; + } + return $args; + } /** * @brief Fetch results @@ -439,7 +479,7 @@ } function getSelectSql($query){ - $with_value = false; + $with_values = false; //$limitOffset = $query->getLimit()->getOffset(); //if($limitOffset) @@ -482,18 +522,18 @@ * it supports a method as navigation **/ function _executeSelectAct($queryObject, $connection = null) { - $query = $this->getSelectSql($queryObject); + $query = $this->getSelectSql($queryObject); - if(strpos($query, "substr")) $query = str_replace ("substr", "substring", $query); + if(strpos($query, "substr")) $query = str_replace ("substr", "substring", $query); - // TODO Decide if we continue to pass parameters like this - $this->param = $queryObject->getArguments(); + // TODO Decide if we continue to pass parameters like this + $this->param = $queryObject->getArguments(); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query, $connection); + $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + $result = $this->_query($query, $connection); - if ($this->isError ()) return $this->queryError($queryObject); - else return $this->queryPageLimit($queryObject, $result, $connection); + if ($this->isError ()) return $this->queryError($queryObject); + else return $this->queryPageLimit($queryObject, $result, $connection); } function getParser(){ @@ -514,63 +554,63 @@ return; } - function queryPageLimit($queryObject, $result, $connection){ - $limit = $queryObject->getLimit(); - if ($limit && $limit->isPageHandler()) { - // Total count + function queryPageLimit($queryObject, $result, $connection) { + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) { + // Total count $temp_where = $queryObject->getWhereString(true, false); - $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE ' . $temp_where)); if ($queryObject->getGroupByString() != '') { $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); } - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $count_query .= (__DEBUG_QUERY__ & 1 && $output->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; $this->param = $queryObject->getArguments(); $result_count = $this->_query($count_query, $connection); $count_output = $this->_fetch($result_count); - $total_count = (int)$count_output->count; + $total_count = (int) $count_output->count; - $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; + $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; // Total pages if ($total_count) { $total_page = (int) (($total_count - 1) / $list_count) + 1; - } else $total_page = 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 Object (); + $buff = new Object (); $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); + $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); return $buff; } $start_count = ($page - 1) * $list_count; - $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $query .= (__DEBUG_QUERY__ & 1 && $queryObject->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; $this->param = $queryObject->getArguments(); - $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); - $virtual_no = $total_count - ($page - 1) * $list_count; - $data = $this->_fetch($result, $virtual_no); - - $buff = new Object (); + $buff = new Object (); $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); - }else{ + }else { $data = $this->_fetch($result); $buff = new Object (); $buff->data = $data; diff --git a/classes/db/DBMysql.class.php b/classes/db/DBMysql.class.php index c5dc6b590..2ca842e37 100644 --- a/classes/db/DBMysql.class.php +++ b/classes/db/DBMysql.class.php @@ -6,6 +6,8 @@ * @version 0.1 * * mysql handling class + * + * Does not use prepared statements, since mysql driver does not support them **/ class DBMysql extends DB { @@ -365,13 +367,8 @@ /** * @brief Handle the insertAct **/ - function _executeInsertAct($queryObject) { - // TODO See what priority does - //priority setting - //$priority = ''; - //if($output->priority) $priority = $output->priority['type'].'_priority'; - - $query = $this->getInsertSql($queryObject, true, true); + function _executeInsertAct($queryObject, $with_values = true) { + $query = $this->getInsertSql($queryObject, $with_values, true); if(is_a($query, 'Object')) return; return $this->_query($query); } @@ -379,13 +376,8 @@ /** * @brief Handle updateAct **/ - function _executeUpdateAct($queryObject) { - // TODO See what proiority does - //priority setting - //$priority = ''; - //if($output->priority) $priority = $output->priority['type'].'_priority'; - - $query = $this->getUpdateSql($queryObject, true, true); + function _executeUpdateAct($queryObject, $with_values = true) { + $query = $this->getUpdateSql($queryObject, $with_values, true); if(is_a($query, 'Object')) return; return $this->_query($query); } @@ -393,15 +385,9 @@ /** * @brief Handle deleteAct **/ - function _executeDeleteAct($queryObject) { - $query = $this->getDeleteSql($queryObject, true, true); - + function _executeDeleteAct($queryObject, $with_values = true) { + $query = $this->getDeleteSql($queryObject, $with_values, true); if(is_a($query, 'Object')) return; - - //priority setting - // TODO Check what priority does - //$priority = ''; - //if($output->priority) $priority = $output->priority['type'].'_priority'; return $this->_query($query); } @@ -411,24 +397,26 @@ * In order to get a list of pages easily when selecting \n * it supports a method as navigation **/ - function _executeSelectAct($queryObject, $connection = null) { - $limit = $queryObject->getLimit(); - if ($limit && $limit->isPageHandler()) - return $this->queryPageLimit($queryObject, $result, $connection); - else { - $query = $this->getSelectSql($queryObject); - if(is_a($query, 'Object')) return; - $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + function _executeSelectAct($queryObject, $connection = null, $with_values = true) { + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) + return $this->queryPageLimit($queryObject, $result, $connection, $with_values); + else { + $query = $this->getSelectSql($queryObject, $with_values); + if (is_a($query, 'Object')) + return; + $query .= (__DEBUG_QUERY__ & 1 && $queryObject->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; - $result = $this->_query ($query, $connection); - if ($this->isError ()) return $this->queryError($queryObject); + $result = $this->_query($query, $connection); + if ($this->isError()) + return $this->queryError($queryObject); - $data = $this->_fetch($result); - $buff = new Object (); - $buff->data = $data; - return $buff; - } - } + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + return $buff; + } + } function db_insert_id() { @@ -463,16 +451,16 @@ return; } - function queryPageLimit($queryObject, $result, $connection){ + function queryPageLimit($queryObject, $result, $connection, $with_values = true){ $limit = $queryObject->getLimit(); // Total count - $temp_where = $queryObject->getWhereString(true, false); - $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + $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(); + $temp_select = $queryObject->getSelectString($with_values); if(strpos(strtolower($temp_select), "distinct") !== false) { - $count_query = sprintf('select %s %s %s', 'FROM ' . $queryObject->getFromString(), $temp_select, ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + $count_query = sprintf('select %s %s %s', 'FROM ' . $queryObject->getFromString($with_values), $temp_select, ($temp_where === '' ? '' : ' WHERE '. $temp_where)); $uses_distinct = true; } @@ -512,7 +500,7 @@ } $start_count = ($page - 1) * $list_count; - $query = $this->getSelectPageSql($queryObject, true, $start_count, $list_count); + $query = $this->getSelectPageSql($queryObject, $with_values, $start_count, $list_count); $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; $result = $this->_query ($query, $connection); diff --git a/classes/db/DBMysql_innodb.class.php b/classes/db/DBMysql_innodb.class.php index fb93ae8c2..d0f117907 100644 --- a/classes/db/DBMysql_innodb.class.php +++ b/classes/db/DBMysql_innodb.class.php @@ -8,6 +8,8 @@ * @version 0.1 * * mysql innodb handling class + * + * Does not use prepared statements since the mysql driver does not support them **/ class DBMysql_innodb extends DBMysql { diff --git a/classes/db/DBMysqli.class.php b/classes/db/DBMysqli.class.php index fe383b6b4..1c53af938 100644 --- a/classes/db/DBMysqli.class.php +++ b/classes/db/DBMysqli.class.php @@ -91,6 +91,44 @@ * return\n **/ function __query($query, $connection) { + if($this->use_prepared_statements == 'Y') + { + // 1. Prepare query + $stmt = mysqli_prepare($connection, $query); + if($stmt){ + $types = ''; + $params = array(); + $this->_prepareQueryParameters($types, $params); + + if(!empty($params)) + { + $args[0] = $stmt; + $args[1] = $types; + + $i = 2; + foreach($params as $key => $param) { + $copy[$key] = $param; + $args[$i++] = &$copy[$key]; + } + + // 2. Bind parameters + $status = call_user_func_array('mysqli_stmt_bind_param',$args); + if(!$status) + $this->setError(-1, "Invalid arguments: $query" . mysqli_error($connection) . PHP_EOL . print_r($args, true)); + } + + // 3. Execute query + $status = mysqli_stmt_execute($stmt); + + if(!$status) + $this->setError(-1, "Prepared statement failed: $query" . mysqli_error($connection) . PHP_EOL . print_r($args, true)); + + // Return stmt for other processing - like retrieving resultset (_fetch) + return $stmt; + // mysqli_stmt_close($stmt); + } + + } // Run the query statement $result = mysqli_query($connection,$query); // Error Check @@ -101,10 +139,161 @@ // Return result return $result; } + + function _prepareQueryParameters(&$types, &$params){ + $types = ''; + $params = array(); + if(!$this->param) return; + + foreach($this->param 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; + } + + + + } + } + + /** + * @brief Fetch results + **/ + function _fetch($result, $arrayIndexEndValue = NULL) { + if($this->use_prepared_statements != 'Y'){ + return parent::_fetch($result, $arrayIndexEndValue); + } + $output = array(); + if(!$this->isConnected() || $this->isError() || !$result) return $output; + + // Prepared stements: bind result variable and fetch data + $stmt = $result; + $meta = mysqli_stmt_result_metadata($stmt); + $fields = mysqli_fetch_fields($meta); + + foreach($fields as $field) + { + if(isset($resultArray[$field->name])) // When joined tables are used and the same column name appears twice, we should add it separately, otherwise bind_result fails + $field->name = 'repeat_' . $field->name; + + // Array passed needs to contain references, not values + $row[$field->name] = ""; + $resultArray[$field->name] = &$row[$field->name]; + } + $resultArray = array_merge(array($stmt), $resultArray); + + call_user_func_array('mysqli_stmt_bind_result', $resultArray); + + $rows = array(); + while(mysqli_stmt_fetch($stmt)) + { + $resultObject = new stdClass(); + + foreach($resultArray as $key => $value) + { + if($key === 0) continue; // Skip stmt object + if(strpos($key, 'repeat_')) $key = substr($key, 6); + $resultObject->$key = $value; + } + + $rows[] = $resultObject; + } + + mysqli_stmt_close($stmt); + + if($arrayIndexEndValue) + { + foreach($rows as $row) + { + $output[$arrayIndexEndValue--] = $row; + } + } + else + { + $output = $rows; + } + + if(count($output)==1){ + if(isset($arrayIndexEndValue)) return $output; + else return $output[0]; + } + + return $output; + } + + function _executeInsertAct($queryObject){ + if($this->use_prepared_statements != 'Y') + { + return parent::_executeInsertAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeInsertAct($queryObject, false); + unset($this->param); + return $result; + } + + function _executeUpdateAct($queryObject) { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeUpdateAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeUpdateAct($queryObject, false); + unset($this->param); + return $result; + } + + function _executeDeleteAct($queryObject) { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeDeleteAct($queryObject); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeDeleteAct($queryObject, false); + unset($this->param); + return $result; + } + + function _executeSelectAct($queryObject, $connection = null) { + if($this->use_prepared_statements != 'Y') + { + return parent::_executeSelectAct($queryObject, $connection); + } + $this->param = $queryObject->getArguments(); + $result = parent::_executeSelectAct($queryObject, $connection, false); + unset($this->param); + return $result; + } function db_insert_id() { - $connection = $this->_getConnection('master'); + $connection = $this->_getConnection('master'); return mysqli_insert_id($connection); } diff --git a/classes/db/queryparts/Query.class.php b/classes/db/queryparts/Query.class.php index 729ca46e4..ad052294f 100644 --- a/classes/db/queryparts/Query.class.php +++ b/classes/db/queryparts/Query.class.php @@ -223,34 +223,36 @@ 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++; + $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++; } - - return trim($where); + + 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); } function getGroupByString(){ @@ -294,6 +296,19 @@ if(!isset($this->arguments)){ $this->arguments = array(); + // Join table arguments + if(count($this->tables) > 0) + { + foreach($this->tables as $table) + { + if($table->isJoinTable()) + { + $args = $table->getArguments(); + if($args) $this->arguments = array_merge($this->arguments, $args); + } + } + } + // Column arguments if(count($this->columns) > 0){ // The if is for delete statements, all others must have columns foreach($this->columns as $column){ diff --git a/classes/db/queryparts/condition/Condition.class.php b/classes/db/queryparts/condition/Condition.class.php index 5fe11adf3..c8e55d0aa 100644 --- a/classes/db/queryparts/condition/Condition.class.php +++ b/classes/db/queryparts/condition/Condition.class.php @@ -24,13 +24,21 @@ } 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; + 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; } function toStringWithoutValue(){ diff --git a/classes/db/queryparts/condition/ConditionWithArgument.class.php b/classes/db/queryparts/condition/ConditionWithArgument.class.php index 5a00153f3..5badb729f 100644 --- a/classes/db/queryparts/condition/ConditionWithArgument.class.php +++ b/classes/db/queryparts/condition/ConditionWithArgument.class.php @@ -14,16 +14,27 @@ } function toStringWithoutValue(){ - $value = $this->argument->getUnescapedValue(); + $value = $this->argument->getUnescapedValue(); - if(is_array($value)){ - $q = ''; - foreach ($value as $v) $q .= '?,'; - if($q !== '') $q = substr($q, 0, -1); - $q = '(' . $q . ')'; - } - else $q = '?'; - return $this->pipe . ' ' . $this->getConditionPart($q); + 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 $this->pipe . ' ' . $this->getConditionPart($q); } function show(){ diff --git a/classes/db/queryparts/table/JoinTable.class.php b/classes/db/queryparts/table/JoinTable.class.php index 7738667d1..47fd265e4 100644 --- a/classes/db/queryparts/table/JoinTable.class.php +++ b/classes/db/queryparts/table/JoinTable.class.php @@ -32,6 +32,15 @@ function isJoinTable(){ return true; } + + function getArguments() + { + $args = array(); + foreach($this->conditions as $conditionGroup) + $args = array_merge($args, $conditionGroup->getArguments()); + return $args; + } + } ?> \ No newline at end of file diff --git a/classes/xml/xmlquery/argument/Argument.class.php b/classes/xml/xmlquery/argument/Argument.class.php index ea1158c39..d44fe36dd 100644 --- a/classes/xml/xmlquery/argument/Argument.class.php +++ b/classes/xml/xmlquery/argument/Argument.class.php @@ -20,7 +20,9 @@ class Argument { function getType() { if (isset($this->type)) + { return $this->type; + } if (is_string($this->value)) return 'column_name'; return 'number'; @@ -29,7 +31,7 @@ class Argument { function setColumnType($value) { $this->type = $value; } - + function setColumnOperation($operation) { $this->column_operation = $operation; } @@ -92,7 +94,7 @@ class Argument { if ($column_type == 'number') { if (is_array($value)) { foreach ($value AS $key => $val) { - if (isset($val)) { + if (isset($val) && $val !== '') { $value[$key] = (int) $val; } } @@ -113,6 +115,13 @@ class Argument { function isValid() { return $this->isValid; } + + function isColumnName(){ + $type = $this->getType(); + if($type == 'column_name') return true; + if($type == 'number' && !is_numeric($this->value) && $this->uses_default_value) return true; + return false; + } function getErrorMessage() { return $this->errorMessage; diff --git a/classes/xml/xmlquery/argument/ConditionArgument.class.php b/classes/xml/xmlquery/argument/ConditionArgument.class.php index f734d2a1d..b4b9bfe7b 100644 --- a/classes/xml/xmlquery/argument/ConditionArgument.class.php +++ b/classes/xml/xmlquery/argument/ConditionArgument.class.php @@ -5,11 +5,11 @@ function ConditionArgument($name, $value, $operation){ - if(isset($value) && in_array($operation, array('in', 'notin', 'between')) && !is_array($value)){ - $value = str_replace(' ', '', $value); - $value = str_replace('\'', '', $value); - $value = explode(',', $value); - } + if(isset($value) && in_array($operation, array('in', 'notin', 'between')) && !is_array($value) && $value != ''){ + $value = str_replace(' ', '', $value); + $value = str_replace('\'', '', $value); + $value = explode(',', $value); + } parent::Argument($name, $value); $this->operation = $operation; } @@ -63,22 +63,33 @@ } } - /** - * 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(){ - return $this->type ? $this->type : (!is_numeric($this->value) ? "varchar" : ""); + /** + * 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){ + function setColumnType($column_type){ if(!isset($this->value)) return; if($column_type === '') return; diff --git a/tests/classes/db/db/xml_query/cubrid/CubridSelectTest.php b/tests/classes/db/db/xml_query/cubrid/CubridSelectTest.php index 31c1f1f78..aef54a3dd 100644 --- a/tests/classes/db/db/xml_query/cubrid/CubridSelectTest.php +++ b/tests/classes/db/db/xml_query/cubrid/CubridSelectTest.php @@ -400,5 +400,45 @@ define('__CUBRID_VERSION__', '8.4.1'); $this->_test($xml_file, $argsString, $expected); } + + + function test_resource_getLatestItem(){ + $xml_file = _TEST_PATH_ . "db/xml_query/cubrid/data/resource.getLatestItem.xml"; + $expected = 'SELECT "package"."module_srl" as "module_srl" + , "package"."status" as "status" + , "package"."category_srl" as "category_srl" + , "package"."member_srl" as "member_srl" + , "package"."package_srl" as "package_srl" + , "package"."path" as "path" + , "package"."license" as "license" + , "package"."title" as "title" + , "package"."homepage" as "homepage" + , "package"."description" as "package_description" + , "package"."voter" as "package_voter" + , "package"."voted" as "package_voted" + , "package"."downloaded" as "package_downloaded" + , "package"."regdate" as "package_regdate" + , "package"."last_update" as "package_last_update" + , "member"."nick_name" as "nick_name" + , "member"."user_id" as "user_id" + , "item"."item_srl" as "item_srl" + , "item"."document_srl" as "document_srl" + , "item"."file_srl" as "item_file_srl" + , "item"."screenshot_url" as "item_screenshot_url" + , "item"."version" as "item_version" + , "item"."voter" as "item_voter" + , "item"."voted" as "item_voted" + , "item"."downloaded" as "item_downloaded" + , "item"."regdate" as "item_regdate" + FROM "xe_resource_packages" as "package" + , "xe_member" as "member" + , "xe_resource_items" as "item" + WHERE "package"."package_srl" = ? + and "package"."member_srl" = "member"."member_srl" + and "item"."item_srl" = "package"."latest_item_srl"'; + $argsString = '$args->package_srl = 18325662;'; + $expectedArgs = array(18325662); + $this->_testPreparedQuery($xml_file, $argsString, $expected, 'getSelectSql', $expectedArgs); + } } \ No newline at end of file diff --git a/tests/classes/db/db/xml_query/cubrid/data/resource.getLatestItem.xml b/tests/classes/db/db/xml_query/cubrid/data/resource.getLatestItem.xml new file mode 100644 index 000000000..f6fa06e83 --- /dev/null +++ b/tests/classes/db/db/xml_query/cubrid/data/resource.getLatestItem.xml @@ -0,0 +1,40 @@ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +