/** * Prépare le texte d'une requête avant son exécution * * Change les préfixes de tables SPIP par ceux véritables * * @param string $query Requête à préparer * @param string $db Nom de la base de donnée * @param string $prefixe Préfixe de tables à appliquer * @return string Requête préparée */ function _mysql_traite_query($query, $db = '', $prefixe = '') { if ($GLOBALS['mysql_rappel_nom_base'] and $db) { $pref = '`' . $db . '`.'; } else { $pref = ''; } if ($prefixe) { $pref .= $prefixe . "_"; } if (!preg_match('/\\s(SET|VALUES|WHERE|DATABASE)\\s/i', $query, $regs)) { $suite = ''; } else { $suite = strstr($query, $regs[0]); $query = substr($query, 0, -strlen($suite)); // propager le prefixe en cas de requete imbriquee // il faut alors echapper les chaine avant de le faire, pour ne pas risquer de // modifier une requete qui est en fait juste du texte dans un champ if (stripos($suite, "SELECT") !== false) { list($suite, $textes) = query_echappe_textes($suite); if (preg_match('/^(.*?)([(]\\s*SELECT\\b.*)$/si', $suite, $r)) { $suite = $r[1] . _mysql_traite_query($r[2], $db, $prefixe); } $suite = query_reinjecte_textes($suite, $textes); } } $r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\\1' . $pref, $query) . $suite; // en option, remplacer les emoji (que mysql ne sait pas gérer) en 💩 if (defined('_MYSQL_NOPLANES') and _MYSQL_NOPLANES and lire_meta('charset_sql_connexion') == 'utf8') { include_spip('inc/charsets'); $r = utf8_noplanes($r); } #spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG); return $r; }
/** * Transformer la requete pour SQLite * * Enlève les textes, transforme la requête pour quelle soit * bien interprétée par SQLite, puis remet les textes * la fonction affecte `$this->query` */ function traduire_requete() { // // 1) Protection des textes en les remplacant par des codes // // enlever les 'textes' et initialiser avec list($this->query, $textes) = query_echappe_textes($this->query); // // 2) Corrections de la requete // // Correction Create Database // Create Database -> requete ignoree if (strpos($this->query, 'CREATE DATABASE') === 0) { spip_log("Sqlite : requete non executee -> {$this->query}", 'sqlite.' . _LOG_AVERTISSEMENT); $this->query = "SELECT 1"; } // Correction Insert Ignore // INSERT IGNORE -> insert (tout court et pas 'insert or replace') if (strpos($this->query, 'INSERT IGNORE') === 0) { spip_log("Sqlite : requete transformee -> {$this->query}", 'sqlite.' . _LOG_DEBUG); $this->query = 'INSERT ' . substr($this->query, '13'); } // Correction des dates avec INTERVAL // utiliser sql_date_proche() de preference if (strpos($this->query, 'INTERVAL') !== false) { $this->query = preg_replace_callback("/DATE_(ADD|SUB)(.*)INTERVAL\\s+(\\d+)\\s+([a-zA-Z]+)\\)/U", array(&$this, '_remplacerDateParTime'), $this->query); } if (strpos($this->query, 'LEFT(') !== false) { $this->query = str_replace('LEFT(', '_LEFT(', $this->query); } if (strpos($this->query, 'TIMESTAMPDIFF(') !== false) { $this->query = preg_replace('/TIMESTAMPDIFF\\(\\s*([^,]*)\\s*,/Uims', "TIMESTAMPDIFF('\\1',", $this->query); } // Correction Using // USING (non reconnu en sqlite2) // problematique car la jointure ne se fait pas du coup. if ($this->sqlite_version == 2 && strpos($this->query, "USING") !== false) { spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", 'sqlite.' . _LOG_ERREUR); $this->query = preg_replace('/USING\\s*\\([^\\)]*\\)/', '', $this->query); } // Correction Field // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END if (strpos($this->query, 'FIELD') !== false) { $this->query = preg_replace_callback('/FIELD\\s*\\(([^\\)]*)\\)/', array(&$this, '_remplacerFieldParCase'), $this->query); } // Correction des noms de tables FROM // mettre les bons noms de table dans from, update, insert, replace... if (preg_match('/\\s(SET|VALUES|WHERE|DATABASE)\\s/iS', $this->query, $regs)) { $suite = strstr($this->query, $regs[0]); $this->query = substr($this->query, 0, -strlen($suite)); } else { $suite = ''; } $pref = $this->prefixe ? $this->prefixe . "_" : ""; $this->query = preg_replace('/([,\\s])spip_/S', '\\1' . $pref, $this->query) . $suite; // Correction zero AS x // pg n'aime pas 0+x AS alias, sqlite, dans le meme style, // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x // il dit que x ne doit pas être un integer dans le order by ! // on remplace du coup x par vide() dans ce cas uniquement // // rien que pour public/vertebrer.php ? if (strpos($this->query, "0 AS") !== false) { // on ne remplace que dans ORDER BY ou GROUP BY if (preg_match('/\\s(ORDER|GROUP) BY\\s/i', $this->query, $regs)) { $suite = strstr($this->query, $regs[0]); $this->query = substr($this->query, 0, -strlen($suite)); // on cherche les noms des x dans 0 AS x // on remplace dans $suite le nom par vide() preg_match_all('/\\b0 AS\\s*([^\\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER); foreach ($matches[1] as $m) { $suite = str_replace($m, 'VIDE()', $suite); } $this->query .= $suite; } } // Correction possible des divisions entieres // Le standard SQL (lequel? ou?) semble indiquer que // a/b=c doit donner c entier si a et b sont entiers 4/3=1. // C'est ce que retournent effectivement SQL Server et SQLite // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333... // // On peut forcer la conversion en multipliant par 1.0 avant la division // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus ! // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228 // http://www.sqlite.org/cvstrac/tktview?tn=3202 // (4*1.0/3) n'est pas rendu dans ce cas ! # $this->query = str_replace('/','* 1.00 / ',$this->query); // Correction critere REGEXP, non reconnu en sqlite2 if ($this->sqlite_version == 2 && strpos($this->query, 'REGEXP') !== false) { $this->query = preg_replace('/([^\\s\\(]*)(\\s*)REGEXP(\\s*)([^\\s\\)]*)/', 'REGEXP($4, $1)', $this->query); } // // 3) Remise en place des textes d'origine // // Correction Antiquotes et echappements // ` => rien if (strpos($this->query, '`') !== false) { $this->query = str_replace('`', '', $this->query); } $this->query = query_reinjecte_textes($this->query, $textes); return $this->query; }
/** * @param $query * @param string $db * @param string $prefixe * @return array|null|string */ function traite_query($query, $db = '', $prefixe = '') { if ($GLOBALS['mysql_rappel_nom_base'] and $db) { $pref = '`' . $db . '`.'; } else { $pref = ''; } if ($prefixe) { $pref .= $prefixe . "_"; } if (!preg_match('/\\s(SET|VALUES|WHERE|DATABASE)\\s/i', $query, $regs)) { $suite = ''; } else { $suite = strstr($query, $regs[0]); $query = substr($query, 0, -strlen($suite)); // propager le prefixe en cas de requete imbriquee // il faut alors echapper les chaine avant de le faire, pour ne pas risquer de // modifier une requete qui est en fait juste du texte dans un champ if (stripos($suite, "SELECT") !== false) { list($suite, $textes) = query_echappe_textes($suite); if (preg_match('/^(.*?)([(]\\s*SELECT\\b.*)$/si', $suite, $r)) { $suite = $r[1] . traite_query($r[2], $db, $prefixe); } $suite = query_reinjecte_textes($suite, $textes); } } $r = preg_replace(_SQL_PREFIXE_TABLE, '\\1' . $pref, $query) . $suite; #spip_log("traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG); return $r; }