/** * Gestion des requêtes ALTER non reconnues de SQLite * * Requêtes non reconnues : * * ALTER TABLE table DROP column * ALTER TABLE table CHANGE [COLUMN] columnA columnB definition * ALTER TABLE table MODIFY column definition * ALTER TABLE table ADD|DROP PRIMARY KEY * * `MODIFY` est transformé en `CHANGE columnA columnA` par spip_sqlite_alter() * * 1) Créer une table B avec le nouveau format souhaité * 2) Copier la table d'origine A vers B * 3) Supprimer la table A * 4) Renommer la table B en A * 5) Remettre les index (qui sont supprimés avec la table A) * * @param string|array $table * - string : Nom de la table table, * - array : couple (nom de la table => nom futur) * @param string|array $colonne * - string : nom de la colonne, * - array : couple (nom de la colonne => nom futur) * @param array $opt * options comme les tables SPIP, qui sera mergé à la table créee : * `array('field'=>array('nom'=>'syntaxe', ...), 'key'=>array('KEY nom'=>'colonne', ...))` * @param string $serveur * Nom de la connexion SQL en cours * @return bool * true si OK, false sinon. */ function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = '') { if (is_array($table)) { reset($table); list($table_origine, $table_destination) = each($table); } else { $table_origine = $table_destination = $table; } // ne prend actuellement qu'un changement // mais pourra etre adapte pour changer plus qu'une colonne a la fois if (is_array($colonne)) { reset($colonne); list($colonne_origine, $colonne_destination) = each($colonne); } else { $colonne_origine = $colonne_destination = $colonne; } if (!isset($opt['field'])) { $opt['field'] = array(); } if (!isset($opt['key'])) { $opt['key'] = array(); } // 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, false, $serveur); if (!$def_origine or !isset($def_origine['field'])) { spip_log("Alter table impossible sur {$table_origine} : table non trouvee", 'sqlite' . _LOG_ERREUR); return false; } $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] = $opt['field'][$colonne_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] = $opt['field'][$colonne_destination]; } // key... $keys = array(); foreach ($def_origine['key'] as $c => $d) { $c = str_replace($colonne_origine, $colonne_destination, $c); $d = str_replace($colonne_origine, $colonne_destination, $d); // seulement si on ne supprime pas la colonne ! if ($d) { $keys[$c] = $d; } } // autres keys, on merge $keys = array_merge($keys, $opt['key']); $queries = array(); // copier dans destination (si differente de origine), sinon tmp $table_copie = $meme_table ? $table_tmp : $table_destination; $autoinc = (isset($keys['PRIMARY KEY']) and $keys['PRIMARY KEY'] and stripos($keys['PRIMARY KEY'], ',') === false and stripos($fields[$keys['PRIMARY KEY']], 'default') === false); if ($q = _sqlite_requete_create($table_copie, $fields, $keys, $autoinc, $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, $temporary = false, $ifnotexists = false, $serveur); $queries[] = "INSERT INTO {$table_destination} SELECT * FROM {$table_copie}"; $queries[] = "DROP TABLE {$table_copie}"; } } // 5) remettre les index ! foreach ($keys as $k => $v) { if ($k == 'PRIMARY KEY') { } else { // enlever KEY $k = substr($k, 4); $queries[] = "CREATE INDEX {$table_destination}" . "_{$k} ON {$table_destination} ({$v})"; } } if (count($queries)) { spip_sqlite::demarrer_transaction($serveur); // il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas foreach ($queries as $q) { if (!spip_sqlite::executer_requete($q, $serveur)) { spip_log(_LOG_GRAVITE_ERREUR, "SQLite : ALTER TABLE table :" . " Erreur a l'execution de la requete : {$q}", 'sqlite'); spip_sqlite::annuler_transaction($serveur); return false; } } spip_sqlite::finir_transaction($serveur); } return true; }
function _sqlite_modifier_table($table, $colonne, $opt=array(), $serveur=''){ if (is_array($table)) { reset($table); list($table_origine,$table_destination) = each($table); } else { $table_origine = $table_destination = $table; } // ne prend actuellement qu'un changement // mais pourra etre adapte pour changer plus qu'une colonne a la fois if (is_array($colonne)) { reset($colonne); list($colonne_origine,$colonne_destination) = each($colonne); } else { $colonne_origine = $colonne_destination = $colonne; } if (!isset($opt['field'])) $opt['field'] = array(); if (!isset($opt['key'])) $opt['key'] = array(); // 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, false, $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] = $opt['field'][$colonne_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] = $opt['field'][$colonne_destination]; } // key... $keys = array(); foreach ($def_origine['key'] as $c=>$d){ $c = str_replace($colonne_origine,$colonne_destination,$c); $d = str_replace($colonne_origine,$colonne_destination,$d); // seulement si on ne supprime pas la colonne ! if ($d) $keys[$c] = $d; } // autres keys, on merge $keys = array_merge($keys,$opt['key']); $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"; } } // 5) remettre les index ! foreach ($keys as $k=>$v) { if ($k=='PRIMARY KEY'){} else { // enlever KEY $k = substr($k,4); $queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)"; } } $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; }