diff --git a/admin-dev/export/.htaccess b/admin-dev/export/.htaccess new file mode 100644 index 000000000..66d49b1bd --- /dev/null +++ b/admin-dev/export/.htaccess @@ -0,0 +1,2 @@ +Order deny,allow +Deny from all diff --git a/admin-dev/export/index.php b/admin-dev/export/index.php new file mode 100644 index 000000000..ab85ebaf4 --- /dev/null +++ b/admin-dev/export/index.php @@ -0,0 +1,36 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 6844 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../"); +exit; diff --git a/admin-dev/requestSql.php b/admin-dev/requestSql.php new file mode 100644 index 000000000..95ee85c83 --- /dev/null +++ b/admin-dev/requestSql.php @@ -0,0 +1,98 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 7310 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +define('PS_ADMIN_DIR', getcwd()); + +include(PS_ADMIN_DIR.'/../config/config.inc.php'); +include(PS_ADMIN_DIR.'/functions.php'); + +$file = 'request_sql_'.Tools::getValue('id_request_sql').'.csv'; +if($csv = fopen(PS_ADMIN_DIR.'/export/'.$file, 'w')) +{ + $sql = RequestSql::getRequestSqlById(Tools::getValue('id_request_sql')); + + if($sql) + { + $results = Db::getInstance()->ExecuteS($sql[0]['sql']); + foreach(array_keys($results[0]) as $key) + { + $tab_key[] = $key; + fputs($csv, $key.';'); + } + foreach($results as $result) + { + fputs($csv, "\n"); + foreach($tab_key as $name) + fputs($csv, $result[$name].';'); + } + if(file_exists(PS_ADMIN_DIR.'/export/'.$file)) + { + $filesize = filesize(PS_ADMIN_DIR.'/export/'.$file); + $upload_max_filesize = return_bytes(ini_get('upload_max_filesize')); + if($filesize < $upload_max_filesize) + { + header("Content-type: text/csv"); + header("Cache-Control: no-store, no-cache"); + header("Content-Disposition: attachment; filename=\"$file\""); + header("Content-Length: ".$filesize); + readfile(PS_ADMIN_DIR.'/export/'.$file); + die(); + } + else + { + header('Location: '.$_SERVER['HTTP_REFERER'].'&maxsize=1'); + die(); + } + } + } + else + { + header('Location: '.$_SERVER['HTTP_REFERER']); + die(); + } +} +else +{ + header('Location: '.$_SERVER['HTTP_REFERER']); + die(); +} + +function return_bytes($val) { + $val = trim($val); + $last = strtolower($val[strlen($val)-1]); + switch($last) { + // Le modifieur 'G' est disponible depuis PHP 5.1.0 + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; +} \ No newline at end of file diff --git a/admin-dev/tabs/AdminImages.php b/admin-dev/tabs/AdminImages.php index 8fce7381e..79ca7a8f0 100644 --- a/admin-dev/tabs/AdminImages.php +++ b/admin-dev/tabs/AdminImages.php @@ -460,11 +460,11 @@ class AdminImages extends AdminTab '.$this->l('Move images').'
'. $this->l('You can choose to keep your images stored in the previous system - nothing wrong with that.').'
'. $this->l('You can also decide to move your images to the new storage system: in this case, click on the "Move images" button below. Please be patient, as this can take several minutes.'). - '

 '. + '

 '. $this->l('After moving all of your product images, for best performance go to the '). ''.$this->l('product preferences tab').''. $this->l(' and set "Activate legacy images compatibility" to NO.').' -

+
'; diff --git a/admin-dev/tabs/AdminRequestSql.php b/admin-dev/tabs/AdminRequestSql.php new file mode 100644 index 000000000..f1fdf2c58 --- /dev/null +++ b/admin-dev/tabs/AdminRequestSql.php @@ -0,0 +1,358 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 6844 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +class AdminRequestSql extends AdminTab +{ + + public function __construct() + { + $this->table = 'request_sql'; + $this->className = 'RequestSql'; + $this->edit = true; + $this->delete = true; + $this->view = true; + $this->export = true; + + $this->fieldsDisplay = array( + 'id_request_sql' => array('title' => $this->l('ID'), 'width' => 25), + 'name' => array('title' => $this->l('Name'), 'width' => 300), + 'sql' => array('title' => $this->l('Request'), 'width' => 500) + ); + parent::__construct(); + } + + public function displayList() + { + return parent::displayList(); + } + + public function displayTop() + { + echo + '
'. + ' '.$this->l('How to create a new sql query?').''. + '
'. + ' +

+
'.$this->l('Warning: when saving the query, only the request type "SELECT" are allowed.').'
'; + + if(isset($_GET['maxsize'])) + { + echo '
'.$this->l('The file is too large and can not be downloaded. Please use the clause "LIMIT" in this query.').'
'; + } + } + + public function displayForm($isMainTab = true) + { + parent::displayForm(); + + if (!($obj = $this->loadObject(true))) + return; + + echo ' +
'.$this->l('Warning: when saving the query, only the request type "SELECT" are allowed.').'
+
+ '.($obj->id ? '' : '').' +
'.$this->l('Request').' + +
+ * +
+ +
+ * +
+
+ +
+
* '.$this->l('Required field').'
+
+
'; + } + + public function postProcess() + { + if (!($obj = $this->loadObject(true))) + return; + + $result = Db::getInstance()->ExecuteS(' + SELECT `id_request_sql` + FROM `'._DB_PREFIX_.'request_sql` + '); + if (sizeof($result) === 1) + foreach ($result AS $row) + $this->_listSkipDelete = array($row['id_request_sql']); + + return parent::postProcess(); + } + + public function _childValidation() + { + if (Tools::getValue('submitAdd'.$this->table) && $sql = Tools::getValue('sql')) + { + $requestSql = new RequestSql(); + $parser = $requestSql->parsingSql($sql); + $validate = $requestSql->validateSql($parser, false, $sql); + + if(!$validate || !empty($requestSql->errorSql)) + $this->_DisplayError($requestSql->errorSql); + } + } + + public function _DisplayError($e) + { + foreach(array_keys($e) as $key) + { + switch($key) + { + case 'checkedFrom': + if(isset($e[$key]['table'])) + $this->_errors[] = Tools::DisplayError($this->l('The Table ').' "'.$e[$key]['table'].'" '.$this->l(' doesn\'t exist.')); + elseif(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedSelect': + if(isset($e[$key]['table'])) + $this->_errors[] = Tools::DisplayError($this->l('The Table ').' "'.$e[$key]['table'].'" '.$this->l(' doesn\'t exist.')); + elseif(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + elseif(isset($e[$key]['*'])) + $this->_errors[] = Tools::DisplayError($this->l('The operand "*" can be used in a nested query.')); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedWhere': + if(isset($e[$key]['operator'])) + $this->_errors[] = Tools::DisplayError($this->l('The operator ').' "'.$e[$key]['operator'].'" '.$this->l(' used is incorrect.')); + elseif(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedHaving': + if(isset($e[$key]['operator'])) + $this->_errors[] = Tools::DisplayError($this->l('The operator ').' "'.$e[$key]['operator'].'" '.$this->l(' used is incorrect.')); + elseif(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedOrder': + if(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedGroupBy': + if(isset($e[$key]['attribut'])) + $this->_errors[] = Tools::DisplayError($this->l('The attribute ').' "'.$e[$key]['attribut'][0].'" '.$this->l(' does not exist in the following tables: ').$e[$key]['attribut'][1].'.'); + else + $this->_errors[] = Tools::DisplayError($this->l('Error')); + break; + case 'checkedLimit': + $this->_errors[] = Tools::DisplayError($this->l('The LIMIT clause must contain numeric arguments.')); + break; + case 'returnNameTable': + if(isset($e[$key]['reference'])) + $this->_errors[] = Tools::DisplayError($this->l('The reference ').'"'.$e[$key]['reference'][0].'"'.$this->l(' doesn\'t exist in : ').$e[$key]['reference'][1]); + else + $this->_errors[] = Tools::DisplayError($this->l('When multiple tables are used, each attribute must be referenced to a table.')); + break; + case 'testedRequired': + $this->_errors[] = Tools::DisplayError($e[$key].' '.$this->l(' doesn\'t exist.')); + break; + case 'testedUnauthorized': + $this->_errors[] = Tools::DisplayError($e[$key].' '.$this->l(' is a unauthorized keyword.')); + break; + default: + break; + } + } + } + + public function viewRequest_sql() + { + if (!($obj = $this->loadObject(true))) + return; + echo '

'.$obj->name.'

'; + + if($results = Db::getInstance()->ExecuteS($obj->sql)) + { + $tab_key = array(); + foreach(array_keys($results[0]) as $key) + $tab_key[] = $key; + echo ' + + '; + foreach($tab_key as $keyName) + echo ''; + echo ' + '; + foreach($results as $result) + { + echo ''; + foreach($tab_key as $name) + echo ''; + echo ''; + } + echo ' +
'.$keyName.'
'.$result[$name].'
+ '; + } + echo '

'.((Tools::getValue('back')) ? $this->l('Back') : $this->l('Back to list')).'
'; + } + + public function displayListContent($token = NULL) + { + /* Display results in a table + * + * align : determine value alignment + * prefix : displayed before value + * suffix : displayed after value + * image : object image + * icon : icon determined by values + * active : allow to toggle status + */ + $id_category = 1; // default categ + + $irow = 0; + if ($this->_list AND isset($this->fieldsDisplay['position'])) + { + $positions = array_map(create_function('$elem', 'return (int)($elem[\'position\']);'), $this->_list); + sort($positions); + } + if ($this->_list) + { + $isCms = false; + if (preg_match('/cms/Ui', $this->identifier)) + $isCms = true; + $keyToGet = 'id_'.($isCms ? 'cms_' : '').'category'.(in_array($this->identifier, array('id_category', 'id_cms_category')) ? '_parent' : ''); + foreach ($this->_list AS $tr) + { + $id = $tr[$this->identifier]; + echo 'identifier,$this->identifiersDnd) ? ' id="tr_'.(($id_category = (int)(Tools::getValue('id_'.($isCms ? 'cms_' : '').'category', '1'))) ? $id_category : '').'_'.$id.'_'.$tr['position'].'"' : '').($irow++ % 2 ? ' class="alt_row"' : '').' '.((isset($tr['color']) AND $this->colorOnBackground) ? 'style="background-color: '.$tr['color'].'"' : '').'> + '; + if ($this->delete AND (!isset($this->_listSkipDelete) OR !in_array($id, $this->_listSkipDelete))) + echo ''; + echo ''; + foreach ($this->fieldsDisplay AS $key => $params) + { + $tmp = explode('!', $key); + $key = isset($tmp[1]) ? $tmp[1] : $tmp[0]; + echo ' + noLink) OR !$this->noLink)) + echo ' onclick="document.location = \''.self::$currentIndex.'&'.$this->identifier.'='.$id.($this->view? '&view' : '&update').$this->table.'&token='.($token!=NULL ? $token : $this->token).'\'">'.(isset($params['prefix']) ? $params['prefix'] : ''); + else + echo '>'; + if (isset($params['active']) AND isset($tr[$key])) + $this->_displayEnableLink($token, $id, $tr[$key], $params['active'], Tools::getValue('id_category'), Tools::getValue('id_product')); + elseif (isset($params['activeVisu']) AND isset($tr[$key])) + echo ''.($tr[$key] ? $this->l('Enabled') : $this->l('Disabled')).''; + elseif (isset($params['position'])) + { + if ($this->_orderBy == 'position' AND $this->_orderWay != 'DESC') + { + echo ' + '.$this->l('Down').''; + + echo ' + '.$this->l('Up').''; } + else + echo (int)($tr[$key] + 1); + } + elseif (isset($tr[$key])) + { + $echo = $tr[$key]; + + echo isset($params['callback']) ? call_user_func_array(array((isset($params['callback_object'])) ? $params['callback_object'] : $this->className, $params['callback']), array($echo, $tr)) : $echo; + } + else + echo '--'; + + echo (isset($params['suffix']) ? $params['suffix'] : ''). + ''; + } + + if ($this->shopLinkType) + { + $name = (Tools::strlen($tr['shop_name']) > 15) ? Tools::substr($tr['shop_name'], 0, 15).'...' : $tr['shop_name']; + echo ''.$name.''; + } + + if ($this->edit OR $this->delete OR ($this->view AND $this->view !== 'noActionColumn')) + { + echo ''; + if ($this->export) + $this->_displayExportLink($token, $id); + if ($this->view) + $this->_displayViewLink($token, $id); + if ($this->edit) + $this->_displayEditLink($token, $id); + if ($this->delete AND (!isset($this->_listSkipDelete) OR !in_array($id, $this->_listSkipDelete))) + $this->_displayDeleteLink($token, $id); + if ($this->duplicate) + $this->_displayDuplicate($token, $id); + echo ''; + } + echo ''; + } + } + } + + protected function _displayExportLink($token = NULL, $id) + { + $_cacheLang['export'] = $this->l('export'); + echo ' + + '.$_cacheLang['export'].''; + } + +} + + diff --git a/classes/RequestSql.php b/classes/RequestSql.php new file mode 100644 index 000000000..2c1243a64 --- /dev/null +++ b/classes/RequestSql.php @@ -0,0 +1,492 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 6844 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +class RequestSql extends ObjectModel +{ + public $name; + public $sql; + + protected $fieldsRequired = array('name', 'sql'); + protected $fieldsSize = array('name' => 200 , 'sql' => 400); + protected $fieldsValidate = array('name' => 'isString', 'sql' => 'isString'); + + protected $table = 'request_sql'; + protected $identifier = 'id_request_sql'; + + public $tested = array('required' => array ('SELECT', 'FROM'), + 'option' => array('WHERE', 'ORDER', 'LIMIT', 'HAVING', 'GROUP'), + 'operator' => array('AND', '&&', 'BETWEEN', 'AND', 'BINARY', '&', '~', '|', '^', 'CASE', 'WHEN', 'END', 'DIV', '/', '<=>', '=', '>=', '>', 'IS', 'NOT', 'NULL', '<<', '<=', '<', 'LIKE', '-', '%', + '!=', '<>', 'REGEXP', '!', '||', 'OR', '+', '>>', 'RLIKE', 'SOUNDS', '*', '-', 'XOR', 'IN'), + 'function' => array('AVG', 'SUM', 'COUNT', 'MIN', 'MAX', 'STDDEV', 'STDDEV_SAMP', 'STDDEV_POP', 'VARIANCE', 'VAR_SAMP', 'VAR_POP', 'GROUP_CONCAT', 'BIT_AND', 'BIT_OR', 'BIT_XOR'), + 'unauthorized' => array('DELETE', 'ALTER', 'INSERT', 'REPLACE', 'CREATE', 'TRUNCATE', 'OPTIMIZE', 'GRANT', 'REVOKE', 'SHOW', 'HANDLER', 'LOAD', 'ROLLBACK', 'SAVEPOINT', 'UNLOCK', 'INSTALL', 'UNINSTALL', 'ANALZYE', 'BACKUP', 'CHECK', 'CHECKSUM', 'REPAIR', 'RESTORE', 'CACHE', 'DESCRIBE', 'EXPLAIN', 'USE', 'HELP', 'SET', 'DUPLICATE', 'VALUES', 'INTO', 'RENAME', 'CALL', 'PROCEDURE', 'FUNCTION', 'DATABASE', 'SERVER', 'LOGFILE', 'DEFINER', 'RETURNS', 'EVENT', 'TABLESPACE', 'VIEW', 'TRIGGER', 'DATA', 'DO', 'PASSWORD', 'USER', 'PLUGIN', 'FLUSH', 'KILL', 'RESET', 'START', 'STOP', 'PURGE', 'EXECUTE', 'PREPARE', 'DEALLOCATE', 'LOCK', 'USING', 'DROP', 'FOR', 'UPDATE', "BEGIN", 'BY', 'ALL', 'SHARE', 'MODE', 'TO', 'KEY', 'DISTINCTROW', 'DISTINCT', 'HIGH_PRIORITY', 'LOW_PRIORITY', 'DELAYED', 'IGNORE', 'FORCE', 'STRAIGHT_JOIN', 'SQL_SMALL_RESULT', 'SQL_BIG_RESULT', 'QUICK', 'SQL_BUFFER_RESULT', 'SQL_CACHE', 'SQL_NO_CACHE', 'SQL_CALC_FOUND_ROWS', 'WITH')); + + public $errorSql = array(); + + public function getFields() + { + parent::validateFields(); + $fields['name'] = pSQL($this->name); + $fields['sql'] = pSQL($this->sql); + return $fields; + } + + public static function getRequestSql() + { + if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('SELECT `name` FROM `'._DB_PREFIX_.'request_sql` ORDER BY `id_request_sql`')) + return false; + $requestSql = array(); + foreach ($result AS $row) + $requestSql[] = $row['sql']; + return $requestSql; + } + + public static function getRequestSqlById($id) + { + return Db::getInstance()->ExecuteS(sprintf('SELECT `sql` FROM `'._DB_PREFIX_.'request_sql` WHERE `id_request_sql` = %s', $id)); + } + + public function parsingSql($sql) + { + return Tools::parserSQL($sql); + } + + public function validateSql($tab, $in = false, $sql) + { + if(!$tab) + return false; + elseif (!$this->testedRequired($tab)) + return false; + elseif (!$this->testedUnauthorized($tab)) + return false; + elseif (!$this->checkedFrom($tab['FROM'])) + return false; + elseif (!$this->checkedSelect($tab['SELECT'], $tab['FROM'], $in)) + { + return false; + } + elseif (isset($tab['WHERE'])) + { + if (!$this->checkedWhere($tab['WHERE'], $tab['FROM'], $this->tested['operator'], $sql)) + return false; + } + elseif (isset($tab['HAVING'])) + { + if (!$this->checkedHaving($tab['HAVING'], $tab['FROM'])) + return false; + } + elseif (isset($tab['ORDER'])) + { + if (!$this->checkedOrder($tab['ORDER'], $tab['FROM'])) + return false; + } + elseif (isset($tab['GROUP'])) + { + if (!$this->checkedGroupBy($tab['GROUP'], $tab['FROM'])) + return false; + } + elseif (isset($tab['LIMIT'])) + { + if (!$this->checkedLimit($tab['LIMIT'])) + return false; + } + + if(empty($this->_errors)) + if(@!Db::getInstance()->ExecuteS($sql)) + return false; + return true; + } + + public function showTables() + { + $results = Db::getInstance()->ExecuteS('SHOW TABLES'); + foreach($results as $result) + { + $key = array_keys($result); + $tables[] = $result[$key[0]]; + } + return $tables; + } + + public function cutJoin($attrs, $from) + { + $attrs = explode('=', str_replace(' ', '', $attrs)); + foreach($attrs as $attr) + { + if($attribut = $this->cutAttribute($attr, $from)) + $tab[] = $attribut; + else + return false; + } + return $tab; + } + + public function cutAttribute($attr, $from) + { + if(preg_match('#^((`(\()?([a-z_])+`(\))?)|((\()?([a-z_])+(\))?))\.((`(\()?([a-z_])+`(\))?)|((\()?([a-z_])+(\))?))$#i', $attr)) + { + $tab = explode('.', str_replace(array('`', '(', ')'), '', $attr)); + if(!$table = $this->returnNameTable($tab[0], $from, $attr)) + return false; + else + return array ('table' => $table, + 'alias' => $tab[0], + 'attribut' => $tab[1], + 'string' => $attr); + } + elseif (preg_match('#^((`(\()?([a-z_])+`(\))?)|((\()?([a-z_])+(\))?))$#i', $attr)) + { + $attribut = str_replace(array('`', '(', ')'), '', $attr); + if(!$table = $this->returnNameTable(false, $from, $attr)) + return false; + else + return array('table' => $table, + 'attribut' => $attribut, + 'string' => $attr); + } + else + return false; + } + + public function returnNameTable($alias = false, $tables, $expr) + { + if($alias) + { + foreach($tables as $table) + { + $tabA['alias'][] = str_replace(array('`', '(', ')'), '', $table['alias']); + $tabA['table'][] = str_replace(array('`', '(', ')'), '', $table['table']); + } + + if(in_array($alias, $tabA['alias'])) + return $tabA['table']; + else + { + $this->errorSql['returnNameTable']['reference'] = array($alias, $expr); + return false; + } + } + elseif(!$alias && (count($tables) > 1)) + { + $this->errorSql['returnNameTable'] = false; + return false; + } + else + { + foreach($tables as $table) + $tab[] = $table['table']; + return $tab; + } + } + + public function attributExistInTable($attr, $tables) + { + foreach($tables as $table) + { + $attributs = Db::getInstance()->ExecuteS(sprintf("DESCRIBE %s", $table)); + foreach($attributs as $attribut) + if ($attribut['Field'] == trim($attr)) + return true; + } + return false; + } + + public function testedRequired($tab) + { + foreach($this->tested['required'] as $key) + if(@!array_key_exists($key, $tab)) + { + $this->errorSql['testedRequired'] = $key; + return false; + } + return true; + } + + public function testedUnauthorized($tab) + { + foreach($this->tested['unauthorized'] as $key) + if(@array_key_exists($key, $tab)) + { + $this->errorSql['testedUnauthorized'] = $key; + return false; + } + return true; + } + + public function checkedFrom($from) + { + for($i = 0 ; $i < count($from) ; $i++) + { + $table = $from[$i]; + if(!in_array(str_replace('`', '', $table['table']), $this->showTables())) + { + $this->errorSql['checkedFrom']['table'] = $table['table']; + return false; + } + if($table['ref_type'] == "ON" && (trim($table['join_type']) == "LEFT" || trim($table['join_type']) == "JOIN")) + { + if($attrs = $this->cutJoin($table['ref_clause'], $from)) + { + foreach($attrs as $attr) + { + if(!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedFrom']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedFrom'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedFrom'] = false; + return false; + } + } + } + } + return true; + } + + + public function checkedSelect($select, $from, $in = false) + { + for($i = 0 ; $i < count($select) ; $i++ ) + { + $attribut = $select[$i]; + if($attribut['base_expr'] != '*') + { + if ($attribut['expr_type'] == "colref" || $attribut['expr_type'] == "reserved") + { + if($attr = $this->cutAttribute($attribut['base_expr'], $from)) + { + if(!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedSelect']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedSelect'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedSelect'] = false; + return false; + } + } + } + } + else + { + if($in) + { + $this->errorSql['checkedSelect']['*'] = false; + return false; + } + } + } + return true; + } + + public function checkedWhere($where, $from, $operator, $sql) + { + for($i = 0 ; $i < count($where) ; $i++ ) + { + $attribut = $where[$i]; + if ($attribut['expr_type'] == "colref" || $attribut['expr_type'] == "reserved") + { + if($attr = $this->cutAttribute($attribut['base_expr'], $from)) + { + if (!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedWhere']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedWhere'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedWhere'] = false; + return false; + } + } + + } + elseif ($attribut['expr_type'] == "operator") + { + if (!in_array(strtoupper($attribut['base_expr']), $this->tested['operator'])) + { + $this->errorSql['checkedWhere']['operator'] = array($attribut['base_expr']); + return false; + } + elseif (!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedWhere']['operator'] = array($attribut['base_expr']); + return false; + } + } + elseif ($attribut['expr_type'] == "subquery") + { + $tab = $attribut['sub_tree']; + return $this->validateSql($tab, true, $sql); + } + } + return true; + } + + public function checkedHaving($having, $from) + { + $nb = count($having); + for($i = 0 ; $i < $nb ; $i++ ) + { + $attribut = $having[$i]; + if($attribut['expr_type'] == "colref") + { + if($attr = $this->cutAttribute($attribut['base_expr'], $from)) + { + if(!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedHaving']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedHaving'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedHaving'] = false; + return false; + } + } + } + + if($attribut['expr_type'] == "operator") + { + if(!in_array(strtoupper($attribut['base_expr']), $this->tested['operator'])) + { + $this->errorSql['checkedHaving']['operator'] = array($attribut['base_expr']); + return false; + } + + } + } + return true; + } + + public function checkedOrder($order, $from) + { + $order = $order[0]; + if($order['type'] == "expression") + { + if($attr = $this->cutAttribute($order['base_expr'], $from)) + { + if(!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedOrder']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedOrder'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedOrder'] = false; + return false; + } + } + } + return true; + } + + public function checkedGroupBy($group, $from) + { + $group = $group[0]; + if($group['type'] == "expression") + { + if($attr = $this->cutAttribute($group['base_expr'], $from)) + { + if(!$this->attributExistInTable($attr['attribut'],$attr['table'])) + { + $this->errorSql['checkedGroupBy']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); + return false; + } + } + else + { + if(isset($this->errorSql['returnNameTable'])) + { + $this->errorSql['checkedGroupBy'] = $this->errorSql['returnNameTable']; + return false; + } + else + { + $this->errorSql['checkedGroupBy'] = false; + return false; + } + } + } + return true; + } + + public function checkedLimit($limit) + { + if(!preg_match('#^[0-9]+$#', trim($limit['start'])) || !preg_match('#^[0-9]+$#', trim($limit['end']))) + { + $this->errorSql['checkedLimit'] = false; + return false; + } + return true; + } + +} + diff --git a/classes/Tools.php b/classes/Tools.php index 793063dcc..32ea1d948 100644 --- a/classes/Tools.php +++ b/classes/Tools.php @@ -1317,6 +1317,18 @@ class ToolsCore return false; } + + public static function parserSQL($sql) + { + if (strlen($sql) > 0) + { + require_once(_PS_TOOL_DIR_.'parser_sql/parser_sql.php'); + $parser = new parserSql($sql); + return $parser->parsed; + } + return false; + } + public static function minifyCSS($css_content, $fileuri = false) { global $current_css_file; diff --git a/install-dev/sql/db.sql b/install-dev/sql/db.sql index 4840df4cd..6b33f30b4 100644 --- a/install-dev/sql/db.sql +++ b/install-dev/sql/db.sql @@ -1392,6 +1392,13 @@ CREATE TABLE `PREFIX_referrer_shop` ( PRIMARY KEY (`id_referrer`, `id_shop`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `PREFIX_request_sql` ( + `id_request_sql` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(200) NOT NULL, + `sql` text NOT NULL, + PRIMARY KEY (`id_request_sql`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; + CREATE TABLE `PREFIX_scene` ( `id_scene` int(10) unsigned NOT NULL auto_increment, `active` tinyint(1) NOT NULL default '1', diff --git a/install-dev/sql/db_settings_extends.sql b/install-dev/sql/db_settings_extends.sql index 8d25424df..22442b27d 100644 --- a/install-dev/sql/db_settings_extends.sql +++ b/install-dev/sql/db_settings_extends.sql @@ -940,6 +940,7 @@ INSERT INTO `PREFIX_access` (`id_profile`, `id_tab`, `view`, `add`, `edit`, `del (2, 89, 0, 0, 0, 0), (2, 90, 0, 0, 0, 0), (2, 91, 0, 0, 0, 0), +(2, 92, 0, 0, 0, 0), (3, 1, 1, 1, 1, 1), (3, 2, 0, 0, 0, 0), (3, 3, 0, 0, 0, 0), @@ -1022,6 +1023,7 @@ INSERT INTO `PREFIX_access` (`id_profile`, `id_tab`, `view`, `add`, `edit`, `del (3, 89, 0, 0, 0, 0), (3, 90, 0, 0, 0, 0), (3, 91, 0, 0, 0, 0), +(3, 92, 0, 0, 0, 0), (4, 1, 1, 1, 1, 1), (4, 2, 1, 1, 1, 1), (4, 3, 1, 1, 1, 1), @@ -1103,7 +1105,8 @@ INSERT INTO `PREFIX_access` (`id_profile`, `id_tab`, `view`, `add`, `edit`, `del (4, 88, 1, 1, 1, 1), (4, 89, 0, 0, 0, 0), (4, 90, 0, 0, 0, 0), -(4, 91, 0, 0, 0, 0); +(4, 91, 0, 0, 0, 0), +(4, 92, 0, 0, 0, 0); INSERT INTO `PREFIX_module_access` (`id_profile`, `id_module`, `configure`, `view`) (SELECT 2, id_module, 0, 1 FROM PREFIX_module); INSERT INTO `PREFIX_module_access` (`id_profile`, `id_module`, `configure`, `view`) (SELECT 3, id_module, 0, 1 FROM PREFIX_module); diff --git a/install-dev/sql/db_settings_lite.sql b/install-dev/sql/db_settings_lite.sql index f8474912e..bfb00cc28 100644 --- a/install-dev/sql/db_settings_lite.sql +++ b/install-dev/sql/db_settings_lite.sql @@ -845,7 +845,7 @@ INSERT INTO `PREFIX_tab` (`id_tab`, `class_name`, `id_parent`, `position`) VALUE (70, 'AdminPerformance', 8, 11),(71, 'AdminCustomerThreads', 29, 4),(72, 'AdminWebservice', 9, 12),(73, 'AdminStockMvt', 1, 9), (80, 'AdminAddonsCatalog', 7, 1),(81, 'AdminAddonsMyAccount', 7, 2),(83, 'AdminThemes', 7, 3),(84, 'AdminGeolocation', 8, 12), (85, 'AdminTaxRulesGroup', 4, 3),(86, 'AdminLogs', 9, 13), (87,'AdminHome',-1,0), -(88,'AdminShop', 0, 11), (89,'AdminGroupShop', 88, 1),(90, 'AdminShopUrl', 88, 2),(91, 'AdminGenders', 2, 4); +(88,'AdminShop', 0, 11), (89,'AdminGroupShop', 88, 1),(90, 'AdminShopUrl', 88, 2),(91, 'AdminGenders', 2, 4),(92, 'AdminRequestSql', 9, 14); INSERT INTO `PREFIX_access` (`id_profile`, `id_tab`, `view`, `add`, `edit`, `delete`) (SELECT 1, id_tab, 1, 1, 1, 1 FROM PREFIX_tab); @@ -862,7 +862,7 @@ INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (1, 61, 'Search Engines'),(1, 62, 'Referrers'),(1, 63, 'Groups'),(1, 64, 'Generators'),(1, 65, 'Shopping Carts'),(1, 66, 'Tags'),(1, 67, 'Search'), (1, 68, 'Attachments'),(1, 69, 'Configuration Information'),(1, 70, 'Performance'),(1, 71, 'Customer Service'),(1, 72, 'Webservice'),(1, 73, 'Stock Movements'), (1, 80, 'Modules & Themes Catalog'),(1, 81, 'My Account'),(1, 82, 'Stores'),(1, 83, 'Themes'),(1, 84, 'Geolocation'),(1, 85, 'Tax Rules'),(1, 86, 'Log'), -(1, 87, 'Home'), (1, 88, 'Shops'), (1, 89, 'Group Shops'), (1, 90, 'Shop Urls'),(1, 91, 'Genders'); +(1, 87, 'Home'), (1, 88, 'Shops'), (1, 89, 'Group Shops'), (1, 90, 'Shop Urls'),(1, 91, 'Genders'),(1, 92, 'Request'); INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (2, 1, 'Catalogue'),(2, 2, 'Clients'),(2, 3, 'Commandes'),(2, 4, 'Paiement'),(2, 5, 'Transport'), @@ -877,7 +877,7 @@ INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (2, 62, 'Sites affluents'),(2, 63, 'Groupes'),(2, 64, 'Générateurs'),(2, 65, 'Paniers'),(2, 66, 'Tags'),(2, 67, 'Recherche'), (2, 68, 'Documents joints'),(2, 69, 'Informations'),(2, 70, 'Performances'),(2, 71, 'SAV'),(2, 72, 'Service web'),(2, 73, 'Mouvements de Stock'), (2, 80, 'Catalogue de modules et thèmes'),(2, 81, 'Mon compte'),(2, 82, 'Magasins'),(2, 83, 'Thèmes'),(2, 84, 'Géolocalisation'),(2, 85, 'Règles de taxes'),(2, 86, 'Log'), -(2, 87,'Accueil'), (2, 88, 'Boutiques'), (2, 89, 'Groupes de boutique'), (2, 90, 'URLs de boutique'),(2, 91, 'Genres'); +(2, 87,'Accueil'), (2, 88, 'Boutiques'), (2, 89, 'Groupes de boutique'), (2, 90, 'URLs de boutique'),(2, 91, 'Genres'),(2, 92, 'requête'); INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (3, 1, 'Catálogo'),(3, 2, 'Clientes'),(3, 3, 'Pedidos'),(3, 4, 'Pago'),(3, 5, 'Transporte'), @@ -891,7 +891,7 @@ INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (3, 55, 'Albaranes de entrega'),(3, 56, 'SEO & URLs'),(3, 57, 'CMS'),(3, 58, 'Mapeo de la imagen'),(3, 59, 'Mensajes del cliente'),(3, 60, 'Rastreo'), (3, 61, 'Motores de búsqueda'),(3, 62, 'Referido'),(3, 63, 'Grupos'),(3, 64, 'Generadores'),(3, 65, 'Carritos'),(3, 66, 'Etiquetas'),(3, 67, 'Búsqueda'),(3, 68, 'Adjuntos'), (3, 69, 'Informaciones'),(3, 70, 'Rendimiento'),(3, 72, 'Web service'),(3, 71, 'Servicio al cliente'),(3, 73, 'Movimiento de Stock'), (3, 82, 'Tiendas'),(3, 83, 'Temas'),(3, 84, 'Geolocalización'),(3, 85, 'Reglas de Impuestos'),(3, 86, 'Log'), -(3, 87,'Home'), (3, 88, 'Shops'), (3, 89, 'Group Shops'), (3, 90, 'Shop Urls'),(3, 91, 'Genders'); +(3, 87,'Home'), (3, 88, 'Shops'), (3, 89, 'Group Shops'), (3, 90, 'Shop Urls'),(3, 91, 'Genders'),(3, 92, 'Solicitud'); INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (4, 1, 'Katalog'),(4, 2, 'Kunden'),(4, 3, 'Bestellungen'),(4, 4, 'Zahlung'), @@ -906,7 +906,7 @@ INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (4, 61, 'Suchmaschinen'),(4, 62, 'Referrer'),(4, 63, 'Gruppen'),(4, 64, 'Generatoren'),(4, 65, 'Warenkörbe'),(4, 66, 'Tags'),(4, 67, 'Suche'), (4, 68, 'Anhänge'),(4, 69, 'Konfigurationsinformationen'),(4, 70, 'Leistung'),(4, 71, 'Kundenservice'),(4, 72, 'Webservice'),(4, 73, 'Lagerbewegungen'), (4, 80, 'Module und Themenkatalog'),(4, 81, 'Mein Konto'),(4, 82, 'Shops'),(4, 83, 'Themen'),(4, 84, 'Geotargeting'),(4, 85, 'Steuerregeln'),(4, 86, 'Log'), -(4, 87,'Home'), (4, 88, 'Shops'), (4, 89, 'Group Shops'), (4, 90, 'Shop Urls'),(4, 91, 'Genders'); +(4, 87,'Home'), (4, 88, 'Shops'), (4, 89, 'Group Shops'), (4, 90, 'Shop Urls'),(4, 91, 'Genders'),(4, 92, 'Wunsch'); INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (5, 1, 'Catalogo'),(5, 2, 'Clienti'),(5, 3, 'Ordini'),(5, 4, 'Pagamento'), @@ -921,7 +921,7 @@ INSERT INTO `PREFIX_tab_lang` (`id_lang`, `id_tab`, `name`) VALUES (5, 61, 'Motori di ricerca'),(5, 62, 'Referenti'),(5, 63, 'Gruppi'),(5, 64, 'Generatori'),(5, 65, 'Carrelli shopping'),(5, 66, 'Tag'),(5, 67, 'Cerca'), (5, 68, 'Allegati'),(5, 69, 'Informazioni di configurazione'),(5, 70, 'Performance'),(5, 71, 'Servizio clienti'),(5, 72, 'Webservice'),(5, 73, 'Movimenti magazzino'), (5, 80, 'Moduli & Temi catalogo'),(5, 81, 'Il mio Account'),(5, 82, 'Negozi'),(5, 83, 'Temi'),(5, 84, 'Geolocalizzazione'),(5, 85, 'Regimi fiscali'),(5, 86, 'Log'), -(5, 87,'Home'), (5, 88, 'Shops'), (5, 89, 'Group Shops'), (5, 90, 'Shop Urls'),(5, 91, 'Genders'); +(5, 87,'Home'), (5, 88, 'Shops'), (5, 89, 'Group Shops'), (5, 90, 'Shop Urls'),(5, 91, 'Genders'),(5, 92, 'Richiesta'); INSERT IGNORE INTO `PREFIX_tab_lang` (`id_tab`, `id_lang`, `name`) (SELECT `id_tab`, id_lang, (SELECT tl.`name` diff --git a/install-dev/sql/upgrade/1.5.0.1.sql b/install-dev/sql/upgrade/1.5.0.1.sql index a3f089ae0..697aa6389 100644 --- a/install-dev/sql/upgrade/1.5.0.1.sql +++ b/install-dev/sql/upgrade/1.5.0.1.sql @@ -157,3 +157,12 @@ ALTER TABLE `PREFIX_attribute_group` ADD `position` INT( 10 ) UNSIGNED NOT NULL ALTER TABLE `PREFIX_feature` ADD `position` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0'; /* PHP:add_feature_position(); */; + +CREATE TABLE IF NOT EXISTS `PREFIX_request_sql` ( + `id_request_sql` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(200) NOT NULL, + `sql` text NOT NULL, + PRIMARY KEY (`id_request_sql`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; + +/* PHP:add_new_tab(AdminRequestSql, fr:Requête|es:Solicitud|en:Request|de:Wunsh|it:Richiesta, 9); */; diff --git a/tools/parser_sql/index.php b/tools/parser_sql/index.php new file mode 100644 index 000000000..d6e8cde4b --- /dev/null +++ b/tools/parser_sql/index.php @@ -0,0 +1,36 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 7776 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../"); +exit; \ No newline at end of file diff --git a/tools/parser_sql/parser_sql.php b/tools/parser_sql/parser_sql.php new file mode 100644 index 000000000..f7e151c9d --- /dev/null +++ b/tools/parser_sql/parser_sql.php @@ -0,0 +1,1970 @@ +load_reserved_words(); + if($sql) $this->parse($sql); + } + + function parse($sql) { + $sql = trim($sql); + + #lex the SQL statement + $in = $this->split_sql($sql); + + #sometimes the parser needs to skip ahead until a particular + #token is found + $skip_until = false; + + #this is the output tree which is being parsed + $out = array(); + + #This is the last type of union used (UNION or UNION ALL) + #indicates a) presence of at least one union in this query + # b) the type of union if this is the first or last query + $union = false; + + #Sometimes a "query" consists of more than one query (like a UNION query) + #this array holds all the queries + $queries=array(); + + #This is the highest level lexical analysis. This is the part of the + #code which finds UNION and UNION ALL query parts + foreach($in as $key => $token) { + $token=trim($token); + + if($skip_until) { + if($token) { + if(strtoupper($token) == $skip_until) { + $skip_until = false; + continue; + } + } else { + continue; + } + } + + if(strtoupper($token) == "UNION") { + $union = 'UNION'; + for($i=$key+1;$i $tok_list) { + foreach($tok_list as $z => $tok) { + $tok = trim($tok); + if(!$tok) continue; + if(preg_match('/^\\(\\s*select\\s*/i', $tok)) { + $queries[$union_type][$i] = $this->parse(substr($tok,1,-1)); + break; + } else { + $queries[$union_type][$i] = $this->process_sql($queries[$union_type][$i]); + break; + } + } + } + } + } + + + /* If there was no UNION or UNION ALL in the query, then the query is + stored at $queries[0]. + */ + if(!empty($queries[0])) { + $queries[0] = $this->process_sql($queries[0]); + + } + + if(count($queries) == 1 && !$union) { + $queries = $queries[0]; + } + + $this->parsed = $queries; + return $this->parsed; + } + + #This function counts open and close parenthesis and + #returns their location. This might be faster as a regex + private function count_paren($token,$chars=array('(',')')) { + $len = strlen($token); + $open=array(); + $close=array(); + for($i=0;$i<$len;++$i){ + if($token[$i] == $chars[0]) { + $open[] = $i; + } elseif($token[$i] == $chars[1]) { + $close[] = $i; + } + + } + return array('open' => $open, 'close' => $close, 'balanced' =>( count($close) - count($open))); + } + + #This function counts open and close parenthesis and + #returns their location. This might be faster as a regex + private function count_backtick($token) { + $len = strlen($token); + $cnt=0; + for($i=0;$i<$len;++$i){ + if($token[$i] == '`') ++$cnt; + } + return $cnt; + } + + #This is the lexer + #this function splits up a SQL statement into easy to "parse" + #tokens for the SQL processor + private function split_sql($sql) { + + if(!is_string($sql)) { + echo "SQL:\n"; + print_r($sql); + exit; + } + + $sql = str_replace(array('\\\'','\\"',"\r\n","\n","()"),array("''",'""'," "," "," "), $sql); + $regex=<<|>|<|&&|\|\||=|\^) +|(\(.*?\)) # Match FUNCTION(...) OR BAREWORDS +|('(?:[^']|'')*'+) +|("(?:[^"]|"")*"+) +|([^ ,]+) +/ix +EOREGEX +; + + $tokens = preg_split($regex, $sql,-1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $token_count = count($tokens); + + /* The above regex has one problem, because the parenthetical match is not greedy. + Thus, when matching grouped expresions such as ( (a and b) or c) the + tokenizer will produce "( (a and b)", " ", "or", " " , "c,")" + + This block detects the number of open/close parens in the given token. If the parens are balanced + (balanced == 0) then we don't need to do anything. + + otherwise, we need to balance the expression. + */ + $reset = false; + for($i=0;$i<$token_count;++$i) { + + if(empty($tokens[$i])) continue; + + $token = $tokens[$i]; + $trim = trim($token); + if($trim) { + if($trim[0] != '(' + && substr($trim,-1) == ')') { + $trim=trim(substr($trim,0, + strpos($trim,'('))); + } + $tokens[$i]=$trim; + $token=$trim; + } + + if($token && $token[0] == '(') { + $info = $this->count_paren($token); + if($info['balanced'] == 0) { + continue; + } + + #we need to find this many closing parens + $needed = abs($info['balanced']); + $n = $i; + while($needed > 0 && $n <$token_count-1) { + ++$n; + #echo "LOOKING FORWARD TO $n [ " . $tokens[$n] . "]\n"; + $token2 = $tokens[$n]; + $info2 = $this->count_paren($token2); + $closes = count($info2['close']); + if($closes != $needed) { + $tokens[$i] .= $tokens[$n]; + unset($tokens[$n]); + $reset = true; + $info2 = $this->count_paren($tokens[$i]); + $needed = abs($info2['balanced']); + # echo "CLOSES LESS THAN NEEDED (still need $needed)\n"; + } else { + /*get the string pos of the last close paren we need*/ + $pos = $info2['close'][count($info2['close'])-1]; + $str1 = $str2 = ""; + if($pos == 0) { + $str1 = ')'; + } else { + $str1 = substr($tokens[$n],0,$pos) . ')'; + $str2 = substr($tokens[$n],$pos+1); + } + #echo "CLOSES FOUND AT $n, offset:$pos [$str1] [$str2]\n"; + if(strlen($str2) > 0) { + $tokens[$n] = $str2; + } else { + unset($tokens[$n]); + $reset = true; + } + $tokens[$i] .= $str1; + $info2 = $this->count_paren($tokens[$i]); + $needed = abs($info2['balanced']); + + } + } + } + } + + #the same problem appears with backticks :( + + /* reset the array if we deleted any tokens above */ + if ($reset) $tokens = array_values($tokens); + + $token_count=count($tokens); + for($i=0;$i<$token_count;++$i) { + if(empty($tokens[$i])) continue; + $token=$tokens[$i]; + $needed=true; + $reset=false; + if($needed && $token && strpos($token,'`') !== false) { + $info = $this->count_backtick($token); + if($info %2 == 0) { #even number of backticks means we are balanced + continue; + } + $needed=1; + + $n = $i; + while($needed && $n <$token_count-1) { + $reset=true; + #echo "BACKTICK COUNT[$i]: $info old: {$tokens[$i]}, new: ($token)\n"; + ++$n; + $token .= $tokens[$n]; + unset($tokens[$n]); + $needed = $this->count_backtick($token) % 2; + } + } + if($reset) $tokens[$i] = $token; + + } + /* reset the array if we deleted any tokens above */ + $tokens = array_values($tokens); + + return $tokens; + + } + + /* This function breaks up the SQL statement into logical sections. + Some sections are then further handled by specialized functions. + */ + private function process_sql(&$tokens,$start_at = 0, $stop_at = false) { + $prev_category = ""; + $start = microtime(true); + $token_category = ""; + + $skip_next=false; + $token_count = count($tokens); + + if(!$stop_at) { + $stop_at = $token_count; + } + + $out = false; + + for($token_number = $start_at;$token_number<$stop_at;++$token_number) { + $token = trim($tokens[$token_number]); + if($token && $token[0] == '(' && $token_category == "") { + $token_category = 'SELECT'; + } + + /* If it isn't obvious, when $skip_next is set, then we ignore the next real + token, that is we ignore whitespace. + */ + if($skip_next) { + #whitespace does not count as a next token + if($token == "") { + continue; + } + + #to skip the token we replace it with whitespace + $new_token = ""; + $skip_next = false; + } + + $upper = strtoupper($token); + switch($upper) { + + /* Tokens that get their own sections. These keywords have subclauses. */ + case 'SELECT': + case 'ORDER': + case 'LIMIT': + case 'SET': + case 'DUPLICATE': + case 'VALUES': + case 'GROUP': + case 'ORDER': + case 'HAVING': + case 'INTO': + case 'WHERE': + case 'RENAME': + case 'CALL': + case 'PROCEDURE': + case 'FUNCTION': + case 'DATABASE': + case 'SERVER': + case 'LOGFILE': + case 'DEFINER': + case 'RETURNS': + case 'EVENT': + case 'TABLESPACE': + case 'VIEW': + case 'TRIGGER': + case 'DATA': + case 'DO': + case 'PASSWORD': + case 'USER': + case 'PLUGIN': + case 'FROM': + case 'FLUSH': + case 'KILL': + case 'RESET': + case 'START': + case 'STOP': + case 'PURGE': + case 'EXECUTE': + case 'PREPARE': + case 'DEALLOCATE': + if($token == 'DEALLOCATE') { + $skip_next = true; + } + /* this FROM is different from FROM in other DML (not join related) */ + if($token_category == 'PREPARE' && $upper == 'FROM') { + continue 2; + } + + $token_category = $upper; + #$join_type = 'JOIN'; + if($upper == 'FROM' && $token_category == 'FROM') { + /* DO NOTHING*/ + } else { + continue 2; + + } + break; + + /* These tokens get their own section, but have no subclauses. + These tokens identify the statement but have no specific subclauses of their own. */ + case 'DELETE': + case 'ALTER': + case 'INSERT': + case 'REPLACE': + case 'TRUNCATE': + case 'CREATE': + case 'TRUNCATE': + case 'OPTIMIZE': + case 'GRANT': + case 'REVOKE': + case 'SHOW': + case 'HANDLER': + case 'LOAD': + case 'ROLLBACK': + case 'SAVEPOINT': + case 'UNLOCK': + case 'INSTALL': + case 'UNINSTALL': + case 'ANALZYE': + case 'BACKUP': + case 'CHECK': + case 'CHECKSUM': + case 'REPAIR': + case 'RESTORE': + case 'CACHE': + case 'DESCRIBE': + case 'EXPLAIN': + case 'USE': + case 'HELP': + $token_category = $upper; /* set the category in case these get subclauses + in a future version of MySQL */ + $out[$upper][0] = $upper; + continue 2; + break; + + /* This is either LOCK TABLES or SELECT ... LOCK IN SHARE MODE*/ + case 'LOCK': + if($token_category == "") { + $token_category = $upper; + $out[$upper][0] = $upper; + } else { + $token = 'LOCK IN SHARE MODE'; + $skip_next=true; + $out['OPTIONS'][] = $token; + } + continue 2; + break; + + case 'USING': + /* USING in FROM clause is different from USING w/ prepared statement*/ + if($token_category == 'EXECUTE') { + $token_category=$upper; + continue 2; + } + if($token_category == 'FROM' && !empty($out['DELETE'])) { + $token_category=$upper; + continue 2; + } + break; + + /* DROP TABLE is different from ALTER TABLE DROP ... */ + case 'DROP': + if($token_category != 'ALTER') { + $token_category = $upper; + $out[$upper][0] = $upper; + continue 2; + } + break; + + case 'FOR': + $skip_next=true; + $out['OPTIONS'][] = 'FOR UPDATE'; + continue 2; + break; + + + case 'UPDATE': + if($token_category == "" ) { + $token_category = $upper; + continue 2; + + } + if($token_category == 'DUPLICATE') { + continue 2; + } + break; + break; + + case 'START': + $token = "BEGIN"; + $out[$upper][0] = $upper; + $skip_next = true; + break; + + /* These tokens are ignored. */ + case 'BY': + case 'ALL': + case 'SHARE': + case 'MODE': + case 'TO': + + case ';': + continue 2; + break; + + case 'KEY': + if($token_category == 'DUPLICATE') { + continue 2; + } + break; + + /* These tokens set particular options for the statement. They never stand alone.*/ + case 'DISTINCTROW': + $token='DISTINCT'; + case 'DISTINCT': + case 'HIGH_PRIORITY': + case 'LOW_PRIORITY': + case 'DELAYED': + case 'IGNORE': + case 'FORCE': + case 'STRAIGHT_JOIN': + case 'SQL_SMALL_RESULT': + case 'SQL_BIG_RESULT': + case 'QUICK': + case 'SQL_BUFFER_RESULT': + case 'SQL_CACHE': + case 'SQL_NO_CACHE': + case 'SQL_CALC_FOUND_ROWS': + $out['OPTIONS'][] = $upper; + continue 2; + break; + + case 'WITH': + if($token_category == 'GROUP') { + $skip_next=true; + $out['OPTIONS'][] = 'WITH ROLLUP'; + continue 2; + } + break; + + + case 'AS': + break; + + case '': + case ',': + case ';': + break; + + default: + break; + } + + if($prev_category == $token_category) { + $out[$token_category][] = $token; + } + + $prev_category = $token_category; + } + + if(!$out) return false; + + + #process the SELECT clause + if(!empty($out['SELECT'])) $out['SELECT'] = $this->process_select($out['SELECT']); + + if(!empty($out['FROM'])) $out['FROM'] = $this->process_from($out['FROM']); + if(!empty($out['USING'])) $out['USING'] = $this->process_from($out['USING']); + if(!empty($out['UPDATE'])) $out['UPDATE'] = $this->process_from($out['UPDATE']); + + if(!empty($out['GROUP'])) $out['GROUP'] = $this->process_group($out['GROUP'], $out['SELECT']); + if(!empty($out['ORDER'])) $out['ORDER'] = $this->process_group($out['ORDER'], $out['SELECT']); + + if(!empty($out['LIMIT'])) $out['LIMIT'] = $this->process_limit($out['LIMIT']); + + if(!empty($out['WHERE'])) $out['WHERE'] = $this->process_expr_list($out['WHERE']); + if(!empty($out['HAVING'])) $out['HAVING'] = $this->process_expr_list($out['HAVING']); + if(!empty($out['SET'])) $out['SET'] = $this->process_set_list($out['SET']); + if(!empty($out['DUPLICATE'])) { + $out['ON DUPLICATE KEY UPDATE'] = $this->process_set_list($out['DUPLICATE']); + unset($out['DUPLICATE']); + } + if(!empty($out['INSERT'])) $out = $this->process_insert($out); + if(!empty($out['REPLACE'])) $out = $this->process_insert($out,'REPLACE'); + if(!empty($out['DELETE'])) $out = $this->process_delete($out); + + return $out; + + } + + /* A SET list is simply a list of key = value expressions separated by comma (,). + This function produces a list of the key/value expressions. + */ + private function process_set_list($tokens) { + $column=""; + $expression=""; + foreach($tokens as $token) { + $token=trim($token); + if(!$column) { + if($token === false || empty($token)) continue; + $column .= $token; + continue; + } + + if($token == '=') continue; + + if($token == ',') { + $expr[] = array('column' => trim($column), 'expr' => trim($expression)); + $expression = $column = ""; + continue; + } + + $expression .= $token; + } + if($expression) { + $expr[] = array('column' => trim($column), 'expr' => trim($expression)); + } + + return $expr; + } + + /* This function processes the LIMIT section. + start,end are set. If only end is provided in the query + then start is set to 0. + */ + private function process_limit($tokens) { + $start = 0; + $end = 0; + + if($pos = array_search(',',$tokens)) { + for($i=0;$i<$pos;++$i) { + if($tokens[$i] != '') { + $start = $tokens[$i]; + break; + } + } + $pos = $pos + 1; + + } else { + $pos = 0; + } + + for($i=$pos;$i $start, 'end' => $end); + } + + /* This function processes the SELECT section. It splits the clauses at the commas. + Each clause is then processed by process_select_expr() and the results are added to + the expression list. + + Finally, at the end, the epxression list is returned. + */ + private function process_select(&$tokens) { + $expression = ""; + $expr = array(); + foreach($tokens as $token) { + if($token == ',') { + $expr[] = $this->process_select_expr(trim($expression)); + $expression = ""; + } else { + if($token === "" || $token===false) $token=" "; + $expression .= $token ; + } + } + if($expression) $expr[] = $this->process_select_expr(trim($expression)); + return $expr; + } + + /* This fuction processes each SELECT clause. We determine what (if any) alias + is provided, and we set the type of expression. + */ + private function process_select_expr($expression) { + $capture = false; + $alias = ""; + $base_expression = $expression; + $upper = trim(strtoupper($expression)); + #if necessary, unpack the expression + if($upper[0] == '(') { + #$expression = substr($expression,1,-1); + $base_expression = $expression; + } + + $tokens = $this->split_sql($expression); + $token_count = count($tokens); + + /* Determine if there is an explicit alias after the AS clause. + If AS is found, then the next non-whitespace token is captured as the alias. + The tokens after (and including) the AS are removed. + */ + $base_expr = ""; + $stripped=array(); + $capture=false; + $alias = ""; + $processed=false; + for($i=0;$i<$token_count;++$i) { + $token = strtoupper($tokens[$i]); + if(trim($token)) { + $stripped[] = $tokens[$i]; + } + + if($token == 'AS') { + unset($tokens[$i]); + $capture = true; + continue; + } + + if($capture) { + if(trim($token)) { + $alias .= $tokens[$i]; + } + unset($tokens[$i]); + continue; + } + $base_expr .= $tokens[$i]; + } + + $stripped = $this->process_expr_list($stripped); + $last = array_pop($stripped); + if(!$alias && $last['expr_type'] == 'colref') { + $prev = array_pop($stripped); + if($prev['expr_type'] == 'operator' || + $prev['expr_type'] == 'const' || + $prev['expr_type'] == 'function' || + $prev['expr_type'] == 'expression' || + #$prev['expr_type'] == 'aggregate_function' || + $prev['expr_type'] == 'subquery' || + $prev['expr_type'] == 'colref') { + $alias = $last['base_expr']; + + #remove the last token + array_pop($tokens); + + $base_expr = join("", $tokens); + + + } + } + + if(!$alias) { + $base_expr=join("", $tokens); + $alias = $base_expr; + } + + /* Properly escape the alias if it is not escaped */ + if ($alias[0] != '`') { + $alias = '`' . str_replace('`','``',$alias) . '`'; + } + $processed = false; + $type='expression'; + + if(substr(trim($base_expr),0,1) == '(') { + $base_expr = substr($expression,1,-1); + if(preg_match('/^sel/i', $base_expr)) { + $type='subquery'; + $processed = $this->parse($base_expr); + } + } + if(!$processed) { + $processed = $this->process_expr_list($tokens); + } + + if(count($processed) == 1) { + $type = $processed[0]['expr_type']; + $processed = false; + } + + return array('expr_type'=>$type,'alias' => $alias, 'base_expr' => $base_expr, 'sub_tree' => $processed); + + } + + + private function process_from(&$tokens) { + + $expression = ""; + $expr = array(); + $token_count=0; + $table = ""; + $alias = ""; + + $skip_next=false; + $i=0; + $join_type = ''; + $ref_type=""; + $ref_expr=""; + $base_expr=""; + $sub_tree = false; + $subquery = ""; + + $first_join=true; + $modifier=""; + $saved_join_type=""; + + foreach($tokens as $token) { + $base_expr = false; + $upper = strtoupper(trim($token)); + + if($skip_next && $token) { + $token_count++; + $skip_next = false; + continue; + } else { + if($skip_next) { + continue; + } + } + + if(preg_match("/^\\s*\\(\\s*select/i",$token)) { + $type = 'subquery'; + $table = "DEPENDENT-SUBQUERY"; + $sub_tree = $this->parse(trim($token,'() ')); + $subquery = $token; + } + + switch($upper) { + case 'OUTER': + case 'LEFT': + case 'RIGHT': + case 'NATURAL': + case 'CROSS': + case ',': + case 'JOIN': + break; + + default: + $expression .= $token == '' ? " " : $token; + if($ref_type) { + $ref_expr .= $token == '' ? " " : $token; + } + break; + } + + switch($upper) { + case 'AS': + $token_count++; + $n=1; + $alias = ""; + while($alias == "") { + $alias = trim($tokens[$i+$n]); + ++$n; + } + + continue; + break; + + case 'INDEX': + if($token_category == 'CREATE') { + $token_category = $upper; + continue 2; + } + + break; + + case 'USING': + case 'ON': + $ref_type = $upper; + $ref_expr = ""; + + case 'CROSS': + case 'USE': + case 'FORCE': + case 'IGNORE': + case 'INNER': + case 'OUTER': + # $expression .= $token; + $token_count++; + continue; + break; + + + + case 'FOR': + $token_count++; + $skip_next = true; + continue; + break; + + case 'LEFT': + case 'RIGHT': + case 'STRAIGHT_JOIN': + $join_type=$saved_join_type; + + $modifier = $upper . " "; + break; + + + case ',': + $modifier = 'CROSS'; + + case 'JOIN': + + if($first_join) { + $join_type = 'JOIN'; + $saved_join_type = ($modifier ? $modifier : 'JOIN'); + } else { + $new_join_type = ($modifier ? $modifier : 'JOIN'); + $join_type = $saved_join_type; + $saved_join_type = $new_join_type; + unset($new_join_type); + } + + $first_join = false; + + if(!trim($alias)) $alias = $table; + + if($subquery) { + $sub_tree = $this->parse(trim($subquery,'()')); + $base_expr=$subquery; + } + + if(substr(trim($table),0,1) == '(') { + $base_expr=trim($table,'() '); + $join_type = 'JOIN'; + $sub_tree = $this->process_from($this->split_sql($base_expr)); + $alias=""; + } + + + if($join_type == "") $join_type='JOIN'; + $expr[] = array('table'=>$table, 'alias'=>$alias,'join_type'=>$join_type,'ref_type'=> $ref_type,'ref_clause'=>trim($ref_expr,'() '), 'base_expr' => $base_expr, 'sub_tree' => $sub_tree); + $modifier = ""; + #$join_type=$saved_join_type; + + + $token_count = 0; + $table = $alias = $expression = $base_expr = $ref_type = $ref_expr = ""; + $sub_tree=false; + $subquery = ""; + + break; + + + default: + if($token === false || empty($token) || $token === "") continue; + + if($token_count == 0 ) { + if(!$table) { + $table = $token ; + } + } else if($token_count == 1) { + $alias = $token; + } + $token_count++; + break; + } + ++$i; + } + if(substr(trim($table),0,1) == '(') { + $base_expr=trim($table,'() '); + $join_type = 'JOIN'; + $sub_tree = $this->process_from($this->split_sql($base_expr)); + $alias = ""; + } else { + if(!trim($alias)) $alias = $table; + } + if($join_type == "") $saved_join_type='JOIN'; + + $expr[] = array('table'=>$table, 'alias'=>$alias,'join_type'=>$saved_join_type,'ref_type'=> $ref_type,'ref_clause'=> trim($ref_expr,'() '), 'base_expr' => $base_expr, 'sub_tree' => $sub_tree); + + + return $expr; + } + + private function process_group(&$tokens, &$select) { + + $out=array(); + $expression = ""; + $direction="ASC"; + $type = "expression"; + if(!$tokens) return false; + + foreach($tokens as $token) { + switch(strtoupper($token)) { + case ',': + $expression = trim($expression); + if($expression[0] != '`' || substr($expression,-1) != '`') { + $escaped = str_replace('`','``',$expression); + } else { + $escaped = $expression; + } + $escaped = '`' . $escaped . '`'; + + if(is_numeric(trim($expression))) { + $type = 'pos'; + } else { + + #search to see if the expression matches an alias + foreach($select as $clause) { + if($clause['alias'] == $escaped) { + $type = 'alias'; + } + } + + if(!$type) $type = "expression"; + } + + $out[]=array('type'=>$type,'base_expr'=>$expression,'direction'=>$direction); + $escaped = ""; + $expression = ""; + $direction = "ASC"; + $type = ""; + break; + + case 'ASC': + $direction = "ASC"; + break; + + case 'DESC': + $direction = "DESC"; + break; + + default: + $expression .= $token == '' ? ' ' : $token; + + + } + } + if($expression) { + $expression = trim($expression); + if($expression[0] != '`' || substr($expression,-1) != '`') { + $escaped = str_replace('`','``',$expression); + } else { + $escaped = $expression; + } + $escaped = '`' . $escaped . '`'; + + if(is_numeric(trim($expression))) { + $type = 'pos'; + } else { + + #search to see if the expression matches an alias + if(!$type && $select) { + foreach($select as $clause) { + if(!is_array($clause)) continue; + if($clause['alias'] == $escaped) { + $type = 'alias'; + } + } + } else { + $type="expression"; + } + + if(!$type) $type = "expression"; + } + + $out[]=array('type'=>$type,'base_expr'=>$expression,'direction'=>$direction); + } + + return $out; + } + + /* Some sections are just lists of expressions, like the WHERE and HAVING clauses. This function + processes these sections. Recursive. + */ + private function process_expr_list($tokens) { + $expr = ""; + $type = ""; + $prev_token = ""; + $skip_next = false; + $sub_expr = ""; + + $in_lists = array(); + foreach($tokens as $key => $token) { + + if(strlen(trim($token))==0) continue; + if($skip_next) { + $skip_next = false; + continue; + } + + $processed = false; + $upper = strtoupper(trim($token)); + if(trim($token)) $token=trim($token); + + /* is it a subquery?*/ + if(preg_match("/^\\s*\\(\\s*SELECT/i", $token)) { + $type = 'subquery'; + #tokenize and parse the subquery. + #we remove the enclosing parenthesis for the tokenizer + $processed = $this->parse(trim($token,' ()')); + + + /* is it an inlist */ + } elseif( $upper[0] == '(' && substr($upper,-1) == ')' ) { + if($prev_token == 'IN') { + $type = "in-list"; + $processed = $this->split_sql(substr($token,1,-1)); + $list = array(); + foreach($processed as $v) { + if($v == ',') continue; + $list[]=$v; + } + $processed = $list; + unset($list); + $prev_token = ""; + + } + elseif($prev_token == 'AGAINST') { + $type = "match-arguments"; + $list = $this->split_sql(substr($token,1,-1)); + if(count($list) > 1){ + $match_mode = implode('',array_slice($list,1)); + $processed = array($list[0], $match_mode); + } + else + $processed = $list[0]; + $prev_token = ""; + } + + /* it is either an operator, a colref or a constant */ + } else { + switch($upper) { + case 'AND': + case '&&': + case 'BETWEEN': + case 'AND': + case 'BINARY': + case '&': + case '~': + case '|': + case '^': + case 'CASE': + case 'WHEN': + case 'END': + case 'DIV': + case '/': + case '<=>': + case '=': + case '>=': + case '>': + case 'IS': + case 'NOT': + case 'NULL': + case '<<': + case '<=': + case '<': + case 'LIKE': + case '-': + case '%': + case '!=': + case '<>': + case 'REGEXP': + case '!': + case '||': + case 'OR': + case '+': + case '>>': + case 'RLIKE': + case 'SOUNDS': + case '*': + case '-': + case 'XOR': + case 'IN': + $processed = false; + $type = "operator"; + break; + default: + switch($token[0]) { + case "'": + case '"': + $type = 'const'; + break; + case '`': + $type = 'colref'; + break; + + default: + if(is_numeric($token)) { + $type = 'const'; + } else { + $type = 'colref'; + } + break; + + } + #$processed = $token; + $processed = false; + } + } + /* is a reserved word? */ + if(($type != 'operator' && $type != 'in-list' && $type != 'sub_expr') && in_array($upper, $this->reserved)) { + $token = $upper; + if(!in_array($upper,$this->functions)) { + $type = 'reserved'; + } else { + switch($token) { + case 'AVG': + case 'SUM': + case 'COUNT': + case 'MIN': + case 'MAX': + case 'STDDEV': + case 'STDDEV_SAMP': + case 'STDDEV_POP': + case 'VARIANCE': + case 'VAR_SAMP': + case 'VAR_POP': + case 'GROUP_CONCAT': + case 'BIT_AND': + case 'BIT_OR': + case 'BIT_XOR': + $type = 'aggregate_function'; + if(!empty($tokens[$key+1])) $sub_expr = $tokens[$key+1]; + #$skip_next=true; + break; + + default: + $type = 'function'; + if(!empty($tokens[$key+1])) $sub_expr = $tokens[$key+1]; else $sub_expr="()"; + #$skip_next=true; + + + break; + } + } + } + + if(!$type) { + if($upper[0] == '(') { + $local_expr = substr(trim($token),1,-1); + } else { + $local_expr = $token; + } + $processed = $this->process_expr_list($this->split_sql($local_expr)); + $type = 'expression'; + + if(count($processed) == 1) { + $type = $processed[0]['expr_type']; + $base_expr = $processed[0]['base_expr']; + $processed = $processed[0]['sub_tree']; + } + + } + + $sub_expr=trim($sub_expr); + $sub_expr = ""; + + $expr[] = array( 'expr_type' => $type, 'base_expr' => $token, 'sub_tree' => $processed); + $prev_token = $upper; + $expr_type = ""; + $type = ""; + } + if($sub_expr) { + $processed['sub_tree'] = $this->process_expr_list($this->split_sql(substr($sub_expr,1,-1))); + } + + if(!is_array($processed)) { + + print_r($processed); + $processed = false; + } + + if($expr_type) { + $expr[] = array( 'expr_type' => $type, 'base_expr' => $token, 'sub_tree' => $processed); + } + $mod = false; + + /* + + for($i=0;$i $tables); + + return $tokens; + } + + function process_insert($tokens, $token_category = 'INSERT') { + $table = ""; + $cols = ""; + + if(isset($tokens['INTO'])) + { + $into = $tokens['INTO']; + foreach($into as $token) { + if(!trim($token)) continue; + if(!$table) { + $table = $token; + }elseif(!$cols) { + $cols = $token; + } + } + } + + if(!$cols) { + $cols = 'ALL'; + } else { + $cols = explode(",", trim($cols,'() ')); + } + unset($tokens['INTO']); + $tokens[$token_category] = array('table'=>$table, 'cols'=>$cols); + return $tokens; + + } + + + function load_reserved_words() { + + $this->functions = array( + 'abs', + 'acos', + 'adddate', + 'addtime', + 'aes_encrypt', + 'aes_decrypt', + 'against', + 'ascii', + 'asin', + 'atan', + 'avg', + 'benchmark', + 'bin', + 'bit_and', + 'bit_or', + 'bitcount', + 'bitlength', + 'cast', + 'ceiling', + 'char', + 'char_length', + 'character_length', + 'charset', + 'coalesce', + 'coercibility', + 'collation', + 'compress', + 'concat', + 'concat_ws', + 'conection_id', + 'conv', + 'convert', + 'convert_tz', + 'cos', + 'cot', + 'count', + 'crc32', + 'curdate', + 'current_user', + 'currval', + 'curtime', + 'database', + 'date_add', + 'date_diff', + 'date_format', + 'date_sub', + 'day', + 'dayname', + 'dayofmonth', + 'dayofweek', + 'dayofyear', + 'decode', + 'default', + 'degrees', + 'des_decrypt', + 'des_encrypt', + 'elt', + 'encode', + 'encrypt', + 'exp', + 'export_set', + 'extract', + 'field', + 'find_in_set', + 'floor', + 'format', + 'found_rows', + 'from_days', + 'from_unixtime', + 'get_format', + 'get_lock', + 'group_concat', + 'greatest', + 'hex', + 'hour', + 'if', + 'ifnull', + 'in', + 'inet_aton', + 'inet_ntoa', + 'insert', + 'instr', + 'interval', + 'is_free_lock', + 'is_used_lock', + 'last_day', + 'last_insert_id', + 'lcase', + 'least', + 'left', + 'length', + 'ln', + 'load_file', + 'localtime', + 'localtimestamp', + 'locate', + 'log', + 'log2', + 'log10', + 'lower', + 'lpad', + 'ltrim', + 'make_set', + 'makedate', + 'maketime', + 'master_pos_wait', + 'match', + 'max', + 'md5', + 'microsecond', + 'mid', + 'min', + 'minute', + 'mod', + 'month', + 'monthname', + 'nextval', + 'now', + 'nullif', + 'oct', + 'octet_length', + 'old_password', + 'ord', + 'password', + 'period_add', + 'period_diff', + 'pi', + 'position', + 'pow', + 'power', + 'quarter', + 'quote', + 'radians', + 'rand', + 'release_lock', + 'repeat', + 'replace', + 'reverse', + 'right', + 'round', + 'row_count', + 'rpad', + 'rtrim', + 'sec_to_time', + 'second', + 'session_user', + 'sha', + 'sha1', + 'sign', + 'soundex', + 'space', + 'sqrt', + 'std', + 'stddev', + 'stddev_pop', + 'stddev_samp', + 'strcmp', + 'str_to_date', + 'subdate', + 'substring', + 'substring_index', + 'subtime', + 'sum', + 'sysdate', + 'system_user', + 'tan', + 'time', + 'timediff', + 'timestamp', + 'timestampadd', + 'timestampdiff', + 'time_format', + 'time_to_sec', + 'to_days', + 'trim', + 'truncate', + 'ucase', + 'uncompress', + 'uncompressed_length', + 'unhex', + 'unix_timestamp', + 'upper', + 'user', + 'utc_date', + 'utc_time', + 'utc_timestamp', + 'uuid', + 'var_pop', + 'var_samp', + 'variance', + 'version', + 'week', + 'weekday', + 'weekofyear', + 'year', + 'yearweek'); + + /* includes functions */ + $this->reserved = array( + 'abs', + 'acos', + 'adddate', + 'addtime', + 'aes_encrypt', + 'aes_decrypt', + 'against', + 'ascii', + 'asin', + 'atan', + 'avg', + 'benchmark', + 'bin', + 'bit_and', + 'bit_or', + 'bitcount', + 'bitlength', + 'cast', + 'ceiling', + 'char', + 'char_length', + 'character_length', + 'charset', + 'coalesce', + 'coercibility', + 'collation', + 'compress', + 'concat', + 'concat_ws', + 'conection_id', + 'conv', + 'convert', + 'convert_tz', + 'cos', + 'cot', + 'count', + 'crc32', + 'curdate', + 'current_user', + 'currval', + 'curtime', + 'database', + 'date_add', + 'date_diff', + 'date_format', + 'date_sub', + 'day', + 'dayname', + 'dayofmonth', + 'dayofweek', + 'dayofyear', + 'decode', + 'default', + 'degrees', + 'des_decrypt', + 'des_encrypt', + 'elt', + 'encode', + 'encrypt', + 'exp', + 'export_set', + 'extract', + 'field', + 'find_in_set', + 'floor', + 'format', + 'found_rows', + 'from_days', + 'from_unixtime', + 'get_format', + 'get_lock', + 'group_concat', + 'greatest', + 'hex', + 'hour', + 'if', + 'ifnull', + 'in', + 'inet_aton', + 'inet_ntoa', + 'insert', + 'instr', + 'interval', + 'is_free_lock', + 'is_used_lock', + 'last_day', + 'last_insert_id', + 'lcase', + 'least', + 'left', + 'length', + 'ln', + 'load_file', + 'localtime', + 'localtimestamp', + 'locate', + 'log', + 'log2', + 'log10', + 'lower', + 'lpad', + 'ltrim', + 'make_set', + 'makedate', + 'maketime', + 'master_pos_wait', + 'match', + 'max', + 'md5', + 'microsecond', + 'mid', + 'min', + 'minute', + 'mod', + 'month', + 'monthname', + 'nextval', + 'now', + 'nullif', + 'oct', + 'octet_length', + 'old_password', + 'ord', + 'password', + 'period_add', + 'period_diff', + 'pi', + 'position', + 'pow', + 'power', + 'quarter', + 'quote', + 'radians', + 'rand', + 'release_lock', + 'repeat', + 'replace', + 'reverse', + 'right', + 'round', + 'row_count', + 'rpad', + 'rtrim', + 'sec_to_time', + 'second', + 'session_user', + 'sha', + 'sha1', + 'sign', + 'soundex', + 'space', + 'sqrt', + 'std', + 'stddev', + 'stddev_pop', + 'stddev_samp', + 'strcmp', + 'str_to_date', + 'subdate', + 'substring', + 'substring_index', + 'subtime', + 'sum', + 'sysdate', + 'system_user', + 'tan', + 'time', + 'timediff', + 'timestamp', + 'timestampadd', + 'timestampdiff', + 'time_format', + 'time_to_sec', + 'to_days', + 'trim', + 'truncate', + 'ucase', + 'uncompress', + 'uncompressed_length', + 'unhex', + 'unix_timestamp', + 'upper', + 'user', + 'utc_date', + 'utc_time', + 'utc_timestamp', + 'uuid', + 'var_pop', + 'var_samp', + 'variance', + 'version', + 'week', + 'weekday', + 'weekofyear', + 'year', + 'yearweek', + 'add', + 'all', + 'alter', + 'analyze', + 'and', + 'as', + 'asc', + 'asensitive', + 'auto_increment', + 'bdb', + 'before', + 'berkeleydb', + 'between', + 'bigint', + 'binary', + 'blob', + 'both', + 'by', + 'call', + 'cascade', + 'case', + 'change', + 'char', + 'character', + 'check', + 'collate', + 'column', + 'columns', + 'condition', + 'connection', + 'constraint', + 'continue', + 'create', + 'cross', + 'current_date', + 'current_time', + 'current_timestamp', + 'cursor', + 'database', + 'databases', + 'day_hour', + 'day_microsecond', + 'day_minute', + 'day_second', + 'dec', + 'decimal', + 'declare', + 'default', + 'delayed', + 'delete', + 'desc', + 'describe', + 'deterministic', + 'distinct', + 'distinctrow', + 'div', + 'double', + 'drop', + 'else', + 'elseif', + 'enclosed', + 'escaped', + 'exists', + 'exit', + 'explain', + 'false', + 'fetch', + 'fields', + 'float', + 'for', + 'force', + 'foreign', + 'found', + 'frac_second', + 'from', + 'fulltext', + 'grant', + 'group', + 'having', + 'high_priority', + 'hour_microsecond', + 'hour_minute', + 'hour_second', + 'if', + 'ignore', + 'in', + 'index', + 'infile', + 'inner', + 'innodb', + 'inout', + 'insensitive', + 'insert', + 'int', + 'integer', + 'interval', + 'into', + 'io_thread', + 'is', + 'iterate', + 'join', + 'key', + 'keys', + 'kill', + 'leading', + 'leave', + 'left', + 'like', + 'limit', + 'lines', + 'load', + 'localtime', + 'localtimestamp', + 'lock', + 'long', + 'longblob', + 'longtext', + 'loop', + 'low_priority', + 'master_server_id', + 'match', + 'mediumblob', + 'mediumint', + 'mediumtext', + 'middleint', + 'minute_microsecond', + 'minute_second', + 'mod', + 'natural', + 'not', + 'no_write_to_binlog', + 'null', + 'numeric', + 'on', + 'optimize', + 'option', + 'optionally', + 'or', + 'order', + 'out', + 'outer', + 'outfile', + 'precision', + 'primary', + 'privileges', + 'procedure', + 'purge', + 'read', + 'real', + 'references', + 'regexp', + 'rename', + 'repeat', + 'replace', + 'require', + 'restrict', + 'return', + 'revoke', + 'right', + 'rlike', + 'second_microsecond', + 'select', + 'sensitive', + 'separator', + 'set', + 'show', + 'smallint', + 'some', + 'soname', + 'spatial', + 'specific', + 'sql', + 'sqlexception', + 'sqlstate', + 'sqlwarning', + 'sql_big_result', + 'sql_calc_found_rows', + 'sql_small_result', + 'sql_tsi_day', + 'sql_tsi_frac_second', + 'sql_tsi_hour', + 'sql_tsi_minute', + 'sql_tsi_month', + 'sql_tsi_quarter', + 'sql_tsi_second', + 'sql_tsi_week', + 'sql_tsi_year', + 'ssl', + 'starting', + 'straight_join', + 'striped', + 'table', + 'tables', + 'terminated', + 'then', + 'timestampadd', + 'timestampdiff', + 'tinyblob', + 'tinyint', + 'tinytext', + 'to', + 'trailing', + 'true', + 'undo', + 'union', + 'unique', + 'unlock', + 'unsigned', + 'update', + 'usage', + 'use', + 'user_resources', + 'using', + 'utc_date', + 'utc_time', + 'utc_timestamp', + 'values', + 'varbinary', + 'varchar', + 'varcharacter', + 'varying', + 'when', + 'where', + 'while', + 'with', + 'write', + 'xor', + 'year_month', + 'zerofill' + ); + + for($i=0;$ireserved);++$i) { + $this->reserved[$i]=strtoupper($this->reserved[$i]); + if(!empty($this->functions[$i])) $this->functions[$i] = strtoupper($this->functions[$i]); + } + } + + } // END CLASS + define('HAVE_PHP_SQL_PARSER',1); +} + diff --git a/translations/fr/admin.php b/translations/fr/admin.php index 52c2c7c84..ef5bfd2db 100644 --- a/translations/fr/admin.php +++ b/translations/fr/admin.php @@ -2495,6 +2495,40 @@ $_LANGADM['AdminReferrers4351cfebe4b61d8aa5efa1d020710005'] = 'Voir'; $_LANGADM['AdminReferrers7dce122004969d56ae2e0245cb754d35'] = 'Modifier'; $_LANGADM['AdminReferrersf9d49c6baa1183b09d4068c3e4d1ba2e'] = 'Supprimer l\'affilié ?'; $_LANGADM['AdminReferrersf2a6c498fb90ee345d997f888fce3b18'] = 'Supprimer'; +$_LANGADM['AdminRequestSqlb718adec73e04ce3ec720dd11a06a308'] = 'ID'; +$_LANGADM['AdminRequestSql49ee3087348e8d44e1feda1917443987'] = 'Nom'; +$_LANGADM['AdminRequestSql15c2d85f1fae22a3c3a0594510a1f611'] = 'Requête'; +$_LANGADM['AdminRequestSqla5d1e00410f8e55885dbb6eddd4fe3cd'] = 'Comment créer une nouvelle requête SQL?'; +$_LANGADM['AdminRequestSqlb8bf3ffcbb8025ef76f8d67fff0cdf2b'] = 'Cliquez sur \"Nouveau\".'; +$_LANGADM['AdminRequestSqlb7ccdf6ab58f5514acc520721ddc9f08'] = 'Remplissez les champs et cliquez sur \"Enregistrer\".'; +$_LANGADM['AdminRequestSqlab5aab7b64571636a2f508cd3ea62e89'] = 'Vous pouvez ensuite afficher les résultats de requête en cliquant sur l\'onglet :'; +$_LANGADM['AdminRequestSqla8ad7f90ed8755a68b8f2c9a583480da'] = 'Vous pouvez aussi exporter les résultats de la requête sous forme de fichier. Csv en cliquant sur l\'onglet :'; +$_LANGADM['AdminRequestSqla08e4672a3def34050a314583dac3e2f'] = 'Attention : Lors de la sauvegarde de la requête, seul les requêtes de type \"SELECT\" sont autorisées.'; +$_LANGADM['AdminRequestSqlb60c0cab3cfd0d38042c8878f2181dc5'] = 'Le fichier est trop grand et ne peut donc pas être téléchargé. Veuillez utilisé la clause \"LIMIT\" dans cette requête.'; +$_LANGADM['AdminRequestSql4e140ba723a03baa6948340bf90e2ef6'] = 'Nom : '; +$_LANGADM['AdminRequestSqlab1d92ebad371934228c8b85f65fa449'] = 'Requête : '; +$_LANGADM['AdminRequestSql38fb7d24e0d60a048f540ecb18e13376'] = 'Sauvegarder'; +$_LANGADM['AdminRequestSql19f823c6453c2b1ffd09cb715214813d'] = 'Champs requis'; +$_LANGADM['AdminRequestSql3ace3d5364e85ed551126b5a788700dd'] = 'La table'; +$_LANGADM['AdminRequestSql97cf45dd5a8ff5a1a1a15f059e25bfc8'] = 'n\'existe pas'; +$_LANGADM['AdminRequestSql70e9732e7c12426a3031cc856aba10c7'] = 'L\'attribut '; +$_LANGADM['AdminRequestSqlf4953e56dea0f7d2efa8592b2cb68e47'] = 'n\'existe pas dans les tables suivantes : '; +$_LANGADM['AdminRequestSql902b0d55fddef6f8d651fe1035b7d4bd'] = 'Votre requête est incorrecte.'; +$_LANGADM['AdminRequestSql1fb2b468d4bc45d026b04629d7367ec5'] = 'L\'opérande \"*\" ne peut être utilisé dans une requete imbriquée.'; +$_LANGADM['AdminRequestSql3a36318229eb9597af8430e8cc12c6e3'] = 'L\'opérateur '; +$_LANGADM['AdminRequestSqlad63922dff7de9001b68aa5ffe98dbbb'] = ' utilisé est incorrecte.'; +$_LANGADM['AdminRequestSql19681d28ed1cc72479bc26b7e76ad240'] = 'La clause LIMIT doit comporter des arguments de type numerique.'; +$_LANGADM['AdminRequestSql569a67022452692cad0c2da1243ad7ab'] = 'La référence '; +$_LANGADM['AdminRequestSqld2c0d63fe01c737e0afe765ffb89fc75'] = 'n\'existe pas dans : '; +$_LANGADM['AdminRequestSql36296325727560bc0b1ae0e0801c82b7'] = 'Lorsque plusieurs tables sont utilisées, chaque attribut doit être référencé à l\'une de ces tables.'; +$_LANGADM['AdminRequestSql627e3d3b6303c563993e54186ffa3fdb'] = 'est un mot clé non autorisé.'; +$_LANGADM['AdminRequestSql0557fa923dcee4d0f86b1409f5c2167f'] = 'Retour'; +$_LANGADM['AdminRequestSql630f6dc397fe74e52d5189e2c80f282b'] = 'Retourner à la liste'; +$_LANGADM['AdminRequestSql00d23a76e43b46dae9ec7aa9dcbebb32'] = 'Activé'; +$_LANGADM['AdminRequestSqlb9f5c797ebbf55adccdd8539a65a0241'] = 'Handicapés'; +$_LANGADM['AdminRequestSql08a38277b0309070706f6652eeae9a53'] = 'Bas'; +$_LANGADM['AdminRequestSql258f49887ef8d14ac268c92b02503aaa'] = 'Jusqu\'à'; +$_LANGADM['AdminRequestSqlb2507468f95156358fa490fd543ad2f0'] = 'Export'; $_LANGADM['AdminReturnb718adec73e04ce3ec720dd11a06a308'] = 'ID'; $_LANGADM['AdminReturnd79cf3f429596f77db95c65074663a54'] = 'ID commande'; $_LANGADM['AdminReturnec53a8c4f07baed5d8825072c89799be'] = 'Statut';