Skip to content
Extraits de code Groupes Projets
Valider 4974f9f2 rédigé par marcimat's avatar marcimat
Parcourir les fichiers

Améliorer la gestion des ALTER TABLE table sous SQLITE

- SQLite2 ne prenait pas en compte (ADD|RENAME) column !
Il faut sous sqlite2 comme pour DROP, MODIFY ou CHANGE, créer une autre table qui contient les changements.

- Les opérations sur DROP, MODIFY, CHANGE, aunsi que pour SQLite2 ADD et RENAME se font maintenant de manière transactionnelle (BEGIN TRANSACTION; les requetes; COMMIT;) de tel sorte que si une erreur survient au cours d'une des requetes, aucun changement ne sera pris en compte.
parent 86535f9b
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -147,10 +147,10 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -147,10 +147,10 @@ function spip_sqlite_alter($query, $serveur=''){
*/ */
// 1 // 1
if (preg_match("/\s*ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*)\s*/i", $query, $regs)){ if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/i", $query, $regs)){
$debut = $regs[0]; $debut = $regs[1];
$table = $regs[2]; $table = $regs[3];
$suite = substr($query, strlen($debut)); $suite = $regs[4];
} else { } else {
spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite'); spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite');
return false; return false;
...@@ -163,7 +163,7 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -163,7 +163,7 @@ function spip_sqlite_alter($query, $serveur=''){
$resultats = array(); $resultats = array();
foreach ($todo as $do){ foreach ($todo as $do){
$do = trim($do); $do = trim($do);
if (!preg_match('/(DROP|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|ADD COLUMN|ADD)\s*([^\s]*)\s*(.*)?/', $do, $matches)){ if (!preg_match('/(DROP|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|RENAME|ADD COLUMN|ADD)\s*([^\s]*)\s*(.*)?/', $do, $matches)){
spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $query", 'sqlite'); spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $query", 'sqlite');
return false; return false;
} }
...@@ -177,11 +177,11 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -177,11 +177,11 @@ function spip_sqlite_alter($query, $serveur=''){
switch($cle){ switch($cle){
// allez, on simule, on simule ! // allez, on simule, on simule !
case 'DROP': case 'DROP':
if (!_sqlite_traiter_alter_table( if (!_sqlite_modifier_table(
$table,
$table, $table,
'DROP',
$colonne_origine, $colonne_origine,
'', '', // colonne origine -> rien !
'', '',
$serveur)){ $serveur)){
return false; return false;
...@@ -195,9 +195,9 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -195,9 +195,9 @@ function spip_sqlite_alter($query, $serveur=''){
$colonne_destination = substr($def, 0, strpos($def,' ')); $colonne_destination = substr($def, 0, strpos($def,' '));
$def = substr($def, strlen($colonne_destination)+1); $def = substr($def, strlen($colonne_destination)+1);
if (!_sqlite_traiter_alter_table( if (!_sqlite_modifier_table(
$table,
$table, $table,
'CHANGE',
$colonne_origine, $colonne_origine,
$colonne_destination, $colonne_destination,
$def, $def,
...@@ -207,33 +207,68 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -207,33 +207,68 @@ function spip_sqlite_alter($query, $serveur=''){
break; break;
case 'MODIFY': case 'MODIFY':
if (!_sqlite_traiter_alter_table( if (!_sqlite_modifier_table(
$table, $table,
'CHANGE', $table,
$colonne_origine,
$colonne_origine, $colonne_origine,
$colonne_origine, // un change sur la meme colonne
$def, $def,
$serveur)){ $serveur)){
return false; return false;
} }
break; break;
// RAS pour ceux la // pas geres en sqlite2
case 'RENAME':
$do = "RENAME TO" . substr($do,6);
case 'RENAME TO': case 'RENAME TO':
if (_sqlite_is_version(3, '', $serveur)){
$requete = new sqlite_traiter_requete("$debut $do", $serveur);
if (!$requete->executer_requete()){
spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite');
return false;
}
// artillerie lourde pour sqlite2 !
} else {
$table_dest = trim(substr($do, 9));
if (!_sqlite_modifier_table($table, $table_dest, '', '', '', $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite');
return false;
}
}
break;
// pas geres en sqlite2
case 'ADD COLUMN': case 'ADD COLUMN':
$do = "ADD".substr($do, 10);
case 'ADD': case 'ADD':
default: default:
$requete = new sqlite_traiter_requete("$debut $do", $serveur); if (_sqlite_is_version(3, '', $serveur)){
if (!$requete->executer_requete()){ $requete = new sqlite_traiter_requete("$debut $do", $serveur);
spip_log("SQLite : Erreur ALTER TABLE (ADD|RENAME) : $query", 'sqlite'); if (!$requete->executer_requete()){
return false; spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite');
} return false;
break; }
break;
// artillerie lourde pour sqlite2 !
} else {
if (preg_match('/^(.*)(BEFORE|AFTER)(.*)$/is', $do, $matches)) {
$do = $matches[1];
}
$def = trim(substr($do, 3));
$colonne_ajoutee = substr($def, 0, strpos($def,' '));
$def = substr($def, strlen($colonne_ajoutee)+1);
if (!_sqlite_modifier_table($table, $table, '', $colonne_ajoutee, $def, $serveur)){
spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite');
return false;
}
}
break;
} }
// tout est bon, ouf ! // tout est bon, ouf !
spip_log("SQLite ($serveur) : Changements OK : $debut $do"); spip_log("SQLite ($serveur) : Changements OK : $debut $do");
} }
spip_log("SQLite ($serveur) : fin ALTER TABLE OK !"); spip_log("SQLite ($serveur) : fin ALTER TABLE OK !");
return true; return true;
} }
...@@ -241,67 +276,9 @@ function spip_sqlite_alter($query, $serveur=''){ ...@@ -241,67 +276,9 @@ function spip_sqlite_alter($query, $serveur=''){
// Fonction de creation d'une table SQL nommee $nom // Fonction de creation d'une table SQL nommee $nom
function spip_sqlite_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='') { function spip_sqlite_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='') {
$query = _sqlite_requete_create($nom, $champs, $cles, $autoinc, $temporary, $ifnotexists=true, $serveur);
$query = $keys = $s = $p = ''; if (!$query) return false;
return spip_sqlite_query($query, $serveur);
// certains plugins declarent les tables (permet leur inclusion dans le dump)
// sans les renseigner (laisse le compilo recuperer la description)
if (!is_array($champs) || !is_array($cles))
return;
// sqlite ne gere pas KEY tout court
foreach($cles as $k => $v) {
if ($k == "PRIMARY KEY"){
$keys .= "$s\n\t\t$k ($v)";
$p = $v;
}
$s = ",";
}
$s = '';
/* a tester ulterieurement
* je ne sais pas a quoi ca sert
*
$character_set = "";
if (@$GLOBALS['meta']['charset_sql_base'])
$character_set .= " CHARACTER SET ".$GLOBALS['meta']['charset_sql_base'];
if (@$GLOBALS['meta']['charset_collation_sql_base'])
$character_set .= " COLLATE ".$GLOBALS['meta']['charset_collation_sql_base'];
*/
$champs = _sqlite_remplacements_definitions_table($champs);
foreach($champs as $k => $v) {
// je sais pas ce que c'est ca...
// puis personne rentre ici vue qe binary->''
/*
if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i',$v,$defs)){
if (preg_match(',(char|text),i',$defs[1]) AND !preg_match(',binary,i',$defs[1]) ){
$v = $defs[1] . $character_set . ' ' . substr($v,strlen($defs[1]));
}
}
*/
// autoincrement v3.1.3 ?
$query .= "$s\n\t\t$k $v";
//. (($autoinc && ($p == $k) && preg_match(',\binteger\b,i', $v))? " AUTOINCREMENT": '');
$s = ",";
}
/* simuler le IF NOT EXISTS - version 2 */
if (_sqlite_is_version(2, '', $serveur)){
$a = spip_sqlite_showtable($nom, $serveur);
if ($a) return false;
}
$temporary = $temporary ? ' TEMPORARY':'';
$ifnotexists = _sqlite_is_version(3, '', $serveur) ? ' IF NOT EXISTS':'';// IF NOT EXISTS
$q = "CREATE$temporary TABLE$ifnotexists $nom ($query" . ($keys ? ",$keys" : '') . ")"
//. ($character_set?" DEFAULT $character_set":"")
."\n";
return spip_sqlite_query($q, $serveur);
} }
...@@ -403,6 +380,7 @@ function spip_sqlite_explain($query, $serveur=''){ ...@@ -403,6 +380,7 @@ function spip_sqlite_explain($query, $serveur=''){
function spip_sqlite_fetch($r, $t='', $serveur='') { function spip_sqlite_fetch($r, $t='', $serveur='') {
$link = _sqlite_link($serveur); $link = _sqlite_link($serveur);
if (!$t) { if (!$t) {
if (_sqlite_is_version(3, $link)) { if (_sqlite_is_version(3, $link)) {
...@@ -486,6 +464,7 @@ function spip_sqlite_insert($table, $champs, $valeurs, $desc='', $serveur='') { ...@@ -486,6 +464,7 @@ function spip_sqlite_insert($table, $champs, $valeurs, $desc='', $serveur='') {
if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table); if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
$t = !isset($_GET['var_profile']) ? 0 : trace_query_start(); $t = !isset($_GET['var_profile']) ? 0 : trace_query_start();
$query="INSERT OR REPLACE INTO $table $champs VALUES $valeurs"; $query="INSERT OR REPLACE INTO $table $champs VALUES $valeurs";
...@@ -656,7 +635,7 @@ function spip_sqlite_showtable($nom_table, $serveur=''){ ...@@ -656,7 +635,7 @@ function spip_sqlite_showtable($nom_table, $serveur=''){
$query = $query =
'SELECT sql FROM' 'SELECT sql FROM'
. '(SELECT * FROM sqlite_master UNION ALL' . ' (SELECT * FROM sqlite_master UNION ALL'
. ' SELECT * FROM sqlite_temp_master)' . ' SELECT * FROM sqlite_temp_master)'
. " WHERE tbl_name LIKE '$nom_table'" . " WHERE tbl_name LIKE '$nom_table'"
. " AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'" . " AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
...@@ -907,6 +886,133 @@ function _sqlite_charger_version($version=''){ ...@@ -907,6 +886,133 @@ function _sqlite_charger_version($version=''){
/*
* Gestion des requetes ALTER non reconnues de SQLite :
* ALTER TABLE table DROP column
* ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
* ALTER TABLE table MODIFY column definition
*
* (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
*
* 1) creer une table B avec le nouveau format souhaite
* 2) copier la table d'origine A vers B
* 3) supprimer la table A
* 4) renommer la table B en A
*
*/
function _sqlite_modifier_table($table_origine, $table_destination, $colonne_origine='', $colonne_destination='', $def_col_destination='', $serveur=''){
// si les noms de tables sont differents, pas besoin de table temporaire
// on prendra directement le nom de la future table
$meme_table = ($table_origine == $table_destination);
$def_origine = sql_showtable($table_origine, $serveur);
$table_tmp = $table_origine . '_tmp';
// 1) creer une table temporaire avec les modifications
// - DROP : suppression de la colonne
// - CHANGE : modification de la colonne
// (foreach pour conserver l'ordre des champs)
// field
$fields = array();
// pour le INSERT INTO plus loin
// stocker la correspondance nouvelles->anciennes colonnes
$fields_correspondances = array();
foreach ($def_origine['field'] as $c=>$d){
if ($colonne_origine && ($c == $colonne_origine)) {
// si pas DROP
if ($colonne_destination){
$fields[$colonne_destination] = $def_col_destination;
$fields_correspondances[$colonne_destination] = $c;
}
} else {
$fields[$c] = $d;
$fields_correspondances[$c] = $c;
}
}
// cas de ADD sqlite2 (ajout du champ en fin de table):
if (!$colonne_origine && $colonne_destination){
$fields[$colonne_destination] = $def_col_destination;
}
// key
$keys = array();
foreach ($def_origine['key'] as $c=>$d){
if ($d == $colonne_origine) {
if ($colonne_destination){
$keys[$c] = $colonne_destination;
}
} else {
$keys[$c] = $d;
}
}
$queries = array();
$queries[] = 'BEGIN TRANSACTION';
// copier dans destination (si differente de origine), sinon tmp
$table_copie = ($meme_table) ? $table_tmp : $table_destination;
if ($q = _sqlite_requete_create(
$table_copie,
$fields,
$keys,
$autoinc=false,
$temporary=false,
$ifnotexists=true,
$serveur)){
$queries[] = $q;
}
// 2) y copier les champs qui vont bien
$champs_dest = join(', ', array_keys($fields_correspondances));
$champs_ori = join(', ', $fields_correspondances);
$queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
// 3) supprimer la table d'origine
$queries[] = "DROP TABLE $table_origine";
// 4) renommer la table temporaire
// avec le nom de la table destination
// si necessaire
if ($meme_table){
if (_sqlite_is_version(3, '', $serveur)){
$queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
} else {
$queries[] = _sqlite_requete_create(
$table_destination,
$fields,
$keys,
$autoinc=false,
$temporary=false,
$ifnotexists=false, // la table existe puisqu'on est dans une transaction
$serveur);
$queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
$queries[] = "DROP TABLE $table_copie";
}
}
$queries[] = "COMMIT";
// il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
foreach ($queries as $q){
$req = new sqlite_traiter_requete($q, $serveur);
if (!$req->executer_requete()){
spip_log("SQLite : ALTER TABLE table :"
." Erreur a l'execution de la requete : $q",'sqlite');
return false;
}
}
return true;
}
/* /*
* Nom des fonctions * Nom des fonctions
*/ */
...@@ -979,103 +1085,75 @@ function _sqlite_remplacements_definitions_table($query){ ...@@ -979,103 +1085,75 @@ function _sqlite_remplacements_definitions_table($query){
return preg_replace(array_keys($remplace), $remplace, $query); return preg_replace(array_keys($remplace), $remplace, $query);
} }
/* /*
* Gestion des requetes ALTER non reconnues de SQLite : * Creer la requete pour la creation d'une table
* ALTER TABLE table DROP column * retourne la requete pour utilisation par sql_create() et sql_alter()
* ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
* ALTER TABLE table MODIFY column definition
*
* (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
*
* 1) creer une table B avec le nouveau format souhaite
* 2) copier la table d'origine A vers B
* 3) supprimer la table A
* 4) renommer la table B en A
*
*/ */
function _sqlite_traiter_alter_table($table, $ordre, $colonne_origine, $colonne_destination='', $def_col_destination='', $serveur=''){ function _sqlite_requete_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $_ifnotexists=true, $serveur='') {
$query = $keys = $s = $p = '';
$def_origine = sql_showtable($table, $serveur);
$table_tmp = $table . '_tmp';
// 1) creer une table temporaire avec les modifications // certains plugins declarent les tables (permet leur inclusion dans le dump)
// - DROP : suppression de la colonne // sans les renseigner (laisse le compilo recuperer la description)
// - CHANGE : modification de la colonne if (!is_array($champs) || !is_array($cles))
// (foreach pour conserver l'ordre des champs) return;
// field
$fields = array();
// pour le INSERT INTO plus loin
// stocker la correspondance nouvelles->anciennes colonnes
$fields_correspondances = array();
foreach ($def_origine['field'] as $c=>$d){ // sqlite ne gere pas KEY tout court
if ($c == $colonne_origine) { foreach($cles as $k => $v) {
// si pas DROP if ($k == "PRIMARY KEY"){
if ($colonne_destination){ $keys .= "$s\n\t\t$k ($v)";
$fields[$colonne_destination] = $def_col_destination; $p = $v;
$fields_correspondances[$colonne_destination] = $c;
}
} else {
$fields[$c] = $d;
$fields_correspondances[$c] = $c;
}
}
// key
$keys = array();
foreach ($def_origine['key'] as $c=>$d){
if ($d == $colonne_origine) {
if ($colonne_destination){
$keys[$c] = $colonne_destination;
}
} else {
$keys[$c] = $d;
} }
$s = ",";
} }
$s = '';
/* a tester ulterieurement
* je ne sais pas a quoi ca sert
*
$character_set = "";
if (@$GLOBALS['meta']['charset_sql_base'])
$character_set .= " CHARACTER SET ".$GLOBALS['meta']['charset_sql_base'];
if (@$GLOBALS['meta']['charset_collation_sql_base'])
$character_set .= " COLLATE ".$GLOBALS['meta']['charset_collation_sql_base'];
*/
if (!sql_create(
$table_tmp,
$fields,
$keys,
$autoinc=false,
$temporary=false,
$serveur)){
spip_log("SQLite : ALTER TABLE table $ordre column :"
.' La creation de la table temporaire a echouee','sqlite');
// si on arrive la, on est plutot mal barre !
return false;
}
// 2) y copier les champs qui vont bien
$champs_dest = join(', ', array_keys($fields_correspondances));
$champs_ori = join(', ', $fields_correspondances);
if (!sql_query("INSERT INTO $table_tmp ($champs_dest) SELECT $champs_ori FROM $table")){
spip_log("SQLite : ALTER TABLE table $ordre column :"
.' La copie de la table d\'origine vers la table temporaire a echouee','sqlite');
return false;
}
// 3) supprimer la table d'origine $champs = _sqlite_remplacements_definitions_table($champs);
if (!sql_query("DROP TABLE $table")){
spip_log("SQLite : ALTER TABLE table $ordre column :" foreach($champs as $k => $v) {
.' La suppression de la table d\'origine a echouee','sqlite'); // je sais pas ce que c'est ca...
return false; // puis personne rentre ici vue qe binary->''
/*
if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i',$v,$defs)){
if (preg_match(',(char|text),i',$defs[1]) AND !preg_match(',binary,i',$defs[1]) ){
$v = $defs[1] . $character_set . ' ' . substr($v,strlen($defs[1]));
}
}
*/
// autoincrement v3.1.3 ?
$query .= "$s\n\t\t$k $v";
//. (($autoinc && ($p == $k) && preg_match(',\binteger\b,i', $v))? " AUTOINCREMENT": '');
$s = ",";
} }
// 4) renommer la table temporaire avec le nom de la table d'origine /* simuler le IF NOT EXISTS - version 2 */
if (!sql_query("ALTER TABLE $table_tmp RENAME TO $table")){ if ($_ifnotexists && _sqlite_is_version(2, '', $serveur)){
spip_log("SQLite : ALTER TABLE table $ordre column :" $a = spip_sqlite_showtable($nom, $serveur);
.' Le renommage de la table temporaire avec le nom de la table d\'origine a echoue','sqlite'); if ($a) return false;
return false;
} }
return true;
}
$temporary = $temporary ? ' TEMPORARY':'';
$ifnotexists = ($_ifnotexists && _sqlite_is_version(3, '', $serveur)) ? ' IF NOT EXISTS':'';// IF NOT EXISTS
$q = "CREATE$temporary TABLE$ifnotexists $nom ($query" . ($keys ? ",$keys" : '') . ")"
//. ($character_set?" DEFAULT $character_set":"")
."\n";
return $q;
}
/* /*
...@@ -1131,7 +1209,7 @@ class sqlite_traiter_requete{ ...@@ -1131,7 +1209,7 @@ class sqlite_traiter_requete{
/* lancer la requete $this->requete et faire le tracage si demande */ /* lancer la requete $this->requete et faire le tracage si demande */
function executer_requete(){ function executer_requete(){
$t = !isset($_GET['var_profile']) ? 0 : trace_query_start(); $t = !isset($_GET['var_profile']) ? 0 : trace_query_start();
#echo("<br /><b>spip_sqlite_query() $serveur >></b> $query"); // boum ? pourquoi ? //echo("<br /><b>executer_requete() $this->serveur >></b> $this->query"); // boum ? pourquoi ?
if ($this->link){ if ($this->link){
if (_sqlite_is_version(3, $this->link)) { if (_sqlite_is_version(3, $this->link)) {
$r = $this->link->query($this->query); $r = $this->link->query($this->query);
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter