WP File Manager
Current Path:
/
home
/
argothem
/
www
/
organecyberpresse
/
ecrire
/
src
/
Sql
/
Sqlite
/
Name
Action
..
PDOStatement.php
Edit
Requeteur.php
Edit
Sqlite.php
Edit
Traducteur.php
Edit
Editing: Traducteur.php
<?php namespace Spip\Sql\Sqlite; /** * Cette classe est presente essentiellement pour un preg_replace_callback * avec des parametres dans la fonction appelee que l'on souhaite incrementer * (fonction pour proteger les textes) */ class Traducteur { /** @var string $query texte de la requête */ public $query = ''; /** @var string $prefixe Préfixe des tables */ public $prefixe = ''; /** @var string $sqlite_version Version de sqlite (2 ou 3) */ public $sqlite_version = ''; /** Pour les corrections à effectuer sur les requêtes : array(code=>'texte') trouvé * * @var array */ public $textes = []; /** * Constructeur * * @param string $query Requête à préparer * @param string $prefixe Prefixe des tables à utiliser * @param string $sqlite_version Version SQLite (2 ou 3) */ public function __construct($query, $prefixe, $sqlite_version) { $this->query = $query; $this->prefixe = $prefixe; $this->sqlite_version = $sqlite_version; } /** * 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` */ public 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', [&$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*\(([^\)]*)\)/', [&$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 // // apparait dans public/vertebrer.php et dans le plugin menu aussi qui genere aussi ce genre de requete via un {par num #GET{tri_num}} // mais est-ce encore un soucis pour sqlite en 2021 ? (ie commenter le preg_replace marche très bien en sqlite 3.28) 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) { if (strpos($suite, $m) !== false) { $suite = preg_replace(",\b$m\b,", '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; } /** * Callback pour remplacer `DATE_` / `INTERVAL` * par `DATE ... strtotime` * * @param array $matches Captures * @return string texte de date compris par SQLite */ public function _remplacerDateParTime($matches) { $op = strtoupper($matches[1] == 'ADD') ? '+' : '-'; return "datetime$matches[2] '$op$matches[3] $matches[4]')"; } /** * Callback pour remplacer `FIELD(table,i,j,k...)` * par `CASE WHEN table=i THEN n ... ELSE 0 END` * * @param array $matches Captures * @return string texte de liste ordonnée compris par SQLite */ public function _remplacerFieldParCase($matches) { $fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X) $t = explode(',', $fields); $index = array_shift($t); $res = ''; $n = 0; foreach ($t as $v) { $n++; $res .= "\nWHEN $index=$v THEN $n"; } return "CASE $res ELSE 0 END "; } }