From aaedf9ad452cbd7f0d9e5a6e6b9dcd5d658437f0 Mon Sep 17 00:00:00 2001
From: Matthieu Marcillaud <marcimat@rezo.net>
Date: Tue, 15 Jan 2008 14:18:24 +0000
Subject: [PATCH] =?UTF-8?q?Ne=20pas=20croire=20=C3=A0=20de=20fausses=20err?=
 =?UTF-8?q?eurs=20SQLite=20avec=20sql=5Ferrno();=20Et=20quelques=20remodel?=
 =?UTF-8?q?ages=20de=20code=20sur=20l'analyse=20des=20requetes=20SQLite.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ecrire/req/sqlite_generique.php | 247 ++++++++++++--------------------
 1 file changed, 89 insertions(+), 158 deletions(-)

diff --git a/ecrire/req/sqlite_generique.php b/ecrire/req/sqlite_generique.php
index 9a41604a45..ff00b59519 100644
--- a/ecrire/req/sqlite_generique.php
+++ b/ecrire/req/sqlite_generique.php
@@ -373,6 +373,9 @@ function spip_sqlite_errno($serveur='') {
 	} else {
 		$s = ": aucune ressource sqlite (link)";	
 	}
+	// si $s = '00000', ne pas croire a une erreur
+	if (!(int)$s) $s = 0;
+	
 	if ($s) spip_log("Erreur sqlite $s");
 
 	return $s;
@@ -728,7 +731,7 @@ function spip_sqlite_updateq($table, $champs, $where='', $desc=array(), $serveur
  */
 
 
-// fonction pour la premiere connexion a un serveur SqLite
+// fonction pour la premiere connexion a un serveur SQLite
 function _sqlite_init(){
 	if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC . 'bases/');
 	if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD);
@@ -1177,8 +1180,13 @@ function spip_versions_sqlite(){
 
 
 /*
- * Class pour partager les lancements de requete 
- * pour sql_query() et sql_alter()
+ * Classe pour partager les lancements de requete
+ * - peut corriger la syntaxe des requetes pour la conformite a sqlite
+ * - peut tracer les requetes
+ * 
+ * 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 sqlite_traiter_requete{
@@ -1190,7 +1198,12 @@ class sqlite_traiter_requete{
 	var $db = ''; // le nom de la base 
 	var $tracer = false; // doit-on tracer les requetes (var_profile)
 	
-	/* constructeur */
+	// Pour les corrections a effectuer sur les requetes :
+	var $textes = array(); 	// array(code=>'texte') trouvé
+	var $codeEchappements = "%@##@%";
+	
+	
+	// constructeur
 	function sqlite_traiter_requete($query, $serveur = ''){
 		$this->query = $query;
 		$this->serveur = $serveur;
@@ -1208,17 +1221,8 @@ class sqlite_traiter_requete{
 	}
 	
 	
-	/* transformer la requete pour sqlite */
-	function traduire_requete(){
-		$analyse = new sqlite_analyse_requete($this->link, $this->query, $this->db, $this->prefixe);
-		// transformer
-		$analyse->creerLesRequetes();
-		// renvoyer
-		$this->query = $analyse->query;
-	}
-	
-	
-	/* lancer la requete $this->requete et faire le tracage si demande */
+	// lancer la requete $this->query,
+	// faire le tracage si demande 
 	function executer_requete(){
 		$t = $this->tracer ? trace_query_start(): 0;
 		//echo("<br /><b>executer_requete() $this->serveur >></b> $this->query"); // boum ? pourquoi ?
@@ -1245,175 +1249,83 @@ class sqlite_traiter_requete{
 			$r = false;	
 		}
 
-		//if (!$r){
-		//	echo "<br /><small>#erreur serveur '$this->serveur' dans &gt; $this->query</small><br />";
-		//	echo "<br />- ".spip_sqlite_error($this->query, $this->serveur);
-		//}
 		if (!$r && $e = spip_sqlite_errno($this->serveur))	// Log de l'erreur eventuelle
 			$e .= spip_sqlite_error($this->query, $this->serveur); // et du fautif
 		
 		return $t ? trace_query_end($this->query, $t, $r, $e, $serveur) : $r;
 	}
-}
-
-
-/*
- * Une classe simplement pour un preg_replace_callback avec des parametres 
- * dans la fonction appelee que l'on souhaite incrementer (fonction pour proteger les textes)
- * 
- * Du coup, je mets aussi les traitements a faire dedans
- * 
- */
-class sqlite_analyse_requete {
-	var $sqlite = ''; 		// la ressource link (ou objet pdo)
-	var $query = ''; 		// la requete
-	var $queryCount = ''; 	// la requete pour comptage des lignes select (sqlite3/PDO)
-	var $db = ''; 			// le nom de la bdd
-	var $prefixe = ''; 		// le prefixe des tables
-	var $debug = false; 	// spip_logguer les actions
-	var $crier = false; 	// echo des actions
-	var $textes = array(); 	// array(code=>'texte') trouvé
-	
-	var $codeEchappements = "%@##@%";
-
-
-	function sqlite_analyse_requete(&$link, $query, $db, $prefixe){
-
-		$this->sqlite 		= $link;
-		$this->query 		= $query;
-		$this->db 			= $db;
-		$this->prefixe 		= $prefixe;
-		$this->queryCount 	= "";
-
-		$this->sqlite_version = _sqlite_is_version('', $this->sqlite);
-	}
-
-
-
-	function creerLesRequetes(){
-		$this->cacherLesTextes();
-		// traitements
-		$this->corrigerTout();
-		// hop, on remet les 'textes'
-		$this->afficherLesTextes();
-	}
 	
-	
-	// enlever le contenu 'texte' des requetes
-	function cacherLesTextes(){
+		
+	// transformer la requete pour sqlite 
+	// enleve les textes, transforme la requete pour quelle soit
+	// bien interpretee 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 echappements ''
 		$this->query = str_replace("''", $this->codeEchappements, $this->query);
 		// enlever les 'textes'
 		$this->textes = array(); // vider 
 		$this->query = preg_replace_callback("/('[^']*')/", array(&$this, '_remplacerTexteParCode'), $this->query);
-	}
-
-
-	// remettre le contenu 'texte' des requetes
-	function afficherLesTextes(){
-		// remettre les 'textes'
-		foreach ($this->textes as $cle=>$val){
-			$this->query = str_replace($cle, $val, $this->query);
-		}
-		// remettre les echappements ''
-		$this->query = str_replace($this->codeEchappements,"''",$this->query);
-	}
-	
-	
-	// les corrections
-	function corrigerTout(){
-		$this->corrigerCreateDatabase();
-		$this->corrigerInsertIgnore();
-		$this->corrigerDate();
-		$this->corrigerUsing();
-		$this->corrigerField();
-		$this->corrigerTablesFrom();
-		$this->corrigerZeroAsX();
-		$this->corrigerAntiquotes();
-		$this->corrigerRegexp();
-	}
-	
-	
-	// ` => rien
-	function corrigerAntiquotes(){
-		$this->query = str_replace('`','',$this->query);	
-	}
-	
-	
-	// Create Database -> ignore
-	function corrigerCreateDatabase(){
+		
+		//
+		// 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");
 			$this->query = "SELECT 1";	
 		}
-	}
-
-
-	// corriger les dates avec INTERVAL
-	function corrigerDate() { 
+		
+		// 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");
+			$this->query = 'INSERT ' . substr($this->query,'13');	
+		}
+		
+		// Correction des dates avec INTERVAL
 		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);
 		}
-	}
-	
-	
-	// FIELD (issu de pg) a tester !
-	function corrigerField(){
+		
+		// Correction Using
+		// USING (inutile et non reconnu en sqlite2)
+		if (($this->sqlite_version == 2) && (strpos($this->query, "USING")!==false)) {
+			$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); 
 		}
-	}
-
-	
-	// INSERT IGNORE -> insert (tout court et pas 'insert or replace')
-	function corrigerInsertIgnore(){
-		if (strpos($this->query, 'INSERT IGNORE')===0){
-			#spip_log("Sqlite : requete transformee -> $this->query","sqlite");
-			$this->query = 'INSERT ' . substr($this->query,'13');	
-		}				
-	}	
-	
-	// critere REGEXP non reconnu en sqlite2
-	function corrigerRegexp(){
-		if (($this->sqlite_version == 2) && (strpos($this->query, 'REGEXP')!==false)){
-			$this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query);
-		}
-	}
-	
-	// mettre les bons noms de table dans from, update, insert, replace...
-	function corrigerTablesFrom(){	
+		
+		// Correction des noms de tables FROM
+		// mettre les bons noms de table dans from, update, insert, replace...
 		if (preg_match('/\s(SET|VALUES|WHERE)\s/i', $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_/', '\1'.$pref, $this->query) . $suite;
-		#spip_log("_sqlite_traite_query: " . substr($this->query,0, 50) . ".... $this->db, $this->prefixe");	
-	}
-		
 		
-	// USING (inutile et non reconnu en sqlite2)
-	function corrigerUsing(){
-		if (($this->sqlite_version == 2) && (strpos($this->query, "USING")!==false)) {
-			$this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query);
-		}
-	}
-
-
-	// pg n'aime pas 0+x AS alias, sqlite, dans le meme style, 
-	// sqlite n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
-	// il dit que x ne doit pas être un integer dans le orger by !
-	// on remplace du coup x par vide() dans ce cas uniquement
-	//
-	// rien que pour public/vertebrer.php ?
-	function corrigerZeroAsX(){
+		// 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 		
+			// 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));
@@ -1427,18 +1339,37 @@ class sqlite_analyse_requete {
 				$this->query .= $suite;
 			}
 		}
+		
+		// Correction Antiquotes
+		// ` => rien
+		$this->query = str_replace('`','',$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
+		//
+		// remettre les 'textes'
+		foreach ($this->textes as $cle=>$val){
+			$this->query = str_replace($cle, $val, $this->query);
+		}
+		// remettre les echappements ''
+		$this->query = str_replace($this->codeEchappements,"''",$this->query);
 	}
 	
-	
-	// les callbacks
-	
+
+
+	// les callbacks	
 	// remplacer DATE_ / INTERVAL par DATE...strtotime
 	function _remplacerDateParTime($matches){
 		$op = strtoupper($matches[1] == 'ADD')?'+':'-';	
 		return "'".date("Y-m-d H:i:s", strtotime(" $op$matches[2] ".strtolower($matches[3])))."'";
-	}	
+	}
 
-	
 	// callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
 	function _remplacerFieldParCase($matches){
 		$fields = substr($matches[0],6,-1); // ne recuperer que l'interieur X de field(X)
@@ -1454,12 +1385,12 @@ class sqlite_analyse_requete {
 		return "CASE $res ELSE 0 END ";			
 	}
 
-
 	// callback ou l'on sauve le texte qui est cache dans un tableau $this->textes
 	function _remplacerTexteParCode($matches){
 		$this->textes[$code = "%@##".count($this->textes)."##@%"] = $matches[1];
 		return $code;	
 	}
-		
+
 }
+
 ?>
-- 
GitLab