Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente Prochaine révision Les deux révisions suivantes | ||
sauvegarde_script_sql [2014/04/08 08:14] tyrtamos |
sauvegarde_script_sql [2014/04/08 10:13] tyrtamos |
||
---|---|---|---|
Ligne 129: | Ligne 129: | ||
Et puis, si j'ai une base de données ayant des contraintes de clés étrangères, | Et puis, si j'ai une base de données ayant des contraintes de clés étrangères, | ||
- | Et si je fais ça, les codes précédents ne fonctionnent plus! Pourquoi? Parce que le " | + | Et si je fais ça, les codes précédents ne fonctionnent plus! Pourquoi? Parce que le " |
Comment corriger cela? En modifiant le module Python! | Comment corriger cela? En modifiant le module Python! | ||
+ | Ce module à modifier s' | ||
+ | Avec les versions récentes de Python 2.7 et Python 3, la modification consiste à supprimer l' | ||
+ | <code python> | ||
+ | q = """ | ||
+ | SELECT " | ||
+ | FROM " | ||
+ | WHERE " | ||
+ | " | ||
+ | ORDER BY " | ||
+ | """ | ||
+ | </ | ||
+ | devient après modification: | ||
+ | <code python> | ||
+ | q = """ | ||
+ | SELECT " | ||
+ | FROM " | ||
+ | WHERE " | ||
+ | " | ||
+ | """ | ||
+ | </ | ||
+ | Dans les versions précédentes de Python, il faut modifier: | ||
+ | <code python> | ||
+ | for table_name, type, sql in sorted(schema_res.fetchall()): | ||
+ | </ | ||
+ | Qui devient: | ||
+ | <code python> | ||
+ | for table_name, type, sql in schema_res.fetchall(): | ||
+ | </ | ||
+ | \\ | ||
+ | Autre problème, mais cette fois-ci avec cx_freeze pour Python 2.7 (pas de problème avec Python 3): | ||
+ | Alors que sans cx_freeze, les lignes de script retournées par iterdump sont correctement encodées, avec cx_freeze, les caractères accentués des données fournissent des erreurs d' | ||
- | J' | + | En fait, alors que les données |
- | Mais il y a un problème | + | La correction est évidente: |
- | La solution | + | <code python> |
+ | from __future__ import unicode_literals | ||
+ | </ | ||
+ | |||
+ | Ce qui dira à l' | ||
+ | |||
+ | Voilà: Après ces corrections, | ||
+ | |||
+ | Si vous ne voulez pas modifier le fichier dump.py, vous pouvez | ||
<code python> | <code python> | ||
- | for table_name, type, sql in sorted(schema_res.fetchall()): | + | from sqlite3dump import _iterdump as iterdump |
</ | </ | ||
- | Qui devient: | + | Il faudra alors modifier dans la 1ère fonction " |
<code python> | <code python> | ||
- | for table_name, type, sql in schema_res.fetchall(): | + | |
+ | </ | ||
+ | |||
+ | par: | ||
+ | |||
+ | <code python> | ||
+ | for i, ligne in enumerate(iterdump(cnx)): | ||
+ | </ | ||
+ | |||
+ | Vous pouvez même ajouter l' | ||
+ | |||
+ | <code python> | ||
+ | cnx = sqlite3.connect(base) | ||
+ | cnx.execute(" | ||
</ | </ | ||
- | Après ça, les tables seront traitées dans l' | + | On va maintenant reconstruire |
- | \\ | + | ==== Conversion d'une base de données sqlite3 en script SQL ==== |
- | Autre problème, mais cette fois-ci avec cx_freeze: | + | |
- | Alors que sans cx_freeze, les lignes de script retournées par iterdump sont correctement encodées, avec cx_freeze, les caractères accentués des données fournissent des erreurs d' | + | <code python> |
+ | #!/ | ||
+ | # -*- coding: utf-8 -*- | ||
+ | # python v2.7 | ||
- | En fait, alors que les données lues dans les tables sont en unicode, ce n'est pas le cas des lignes de script retournées par iterdump. | + | import os |
+ | import codecs | ||
+ | import sqlite3 | ||
- | La correction est évidente: dans toutes les lignes | + | from sqlite3dump import _iterdump as iterdump |
+ | |||
+ | ############################################################################# | ||
+ | def base2script(base, | ||
+ | """ | ||
+ | base: la base de données (nom du fichier | ||
+ | | ||
+ | | ||
+ | | ||
+ | # ouvre la base de données sqlite3 | ||
+ | try: | ||
+ | cnx = sqlite3.connect(base) | ||
+ | cnx.execute(" | ||
+ | except sqlite3.Error, err: | ||
+ | print u" | ||
+ | return | ||
+ | |||
+ | # convertit la base sqlite3 en script SQL | ||
+ | with codecs.open(script, 'w', codage) as f: | ||
+ | for i, ligne in enumerate(iterdump(cnx)): | ||
+ | f.write(u'%s\n' % (ligne,)) | ||
+ | |||
+ | # ferme la base | ||
+ | cnx.close() | ||
+ | </ | ||
+ | |||
+ | ==== Conversion d'un script SQL en base de données sqlite3 ==== | ||
<code python> | <code python> | ||
- | yield('{0};'.format(sql)) | + | # |
+ | # -*- coding: utf-8 -*- | ||
+ | # python v2.7 | ||
+ | |||
+ | import os | ||
+ | import codecs | ||
+ | import sqlite3 | ||
+ | |||
+ | from sqlite3dump import _iterdump as iterdump | ||
+ | |||
+ | ############################################################################# | ||
+ | def script2base(script, base, codage='utf-8-sig' | ||
+ | """ | ||
+ | | ||
+ | base: la base de données (nom du fichier avec son chemin) | ||
+ | | ||
+ | """ | ||
+ | # lit et charge en mémoire le script SQL | ||
+ | with codecs.open(script, | ||
+ | scriptsql = f.read() | ||
+ | |||
+ | # supprime la base si elle existe déjà | ||
+ | if os.path.exists(base): | ||
+ | os.remove(base) | ||
+ | |||
+ | # ouvre la base de données sqlite3 et crée un curseur | ||
+ | try: | ||
+ | cnx = sqlite3.connect(base) | ||
+ | cnx.execute(" | ||
+ | cur = cnx.cursor() | ||
+ | except sqlite3.Error, | ||
+ | print u" | ||
+ | return | ||
+ | |||
+ | # exécute le script pour reconstruire la base de données sqlite3 | ||
+ | try: | ||
+ | cur.executescript(scriptsql) | ||
+ | except sqlite3.Error, | ||
+ | print u" | ||
+ | cur.close() | ||
+ | cnx.close() | ||
+ | return | ||
+ | |||
+ | # ferme la base de données | ||
+ | cur.close() | ||
+ | cnx.close() | ||
</ | </ | ||
- | devient: | + | ==== Tester la validité d'une modification de script SQL ==== |
+ | |||
+ | Voilà le code proposé pour tester si un script modifié respecte bien les contraintes d' | ||
+ | |||
+ | Au lieu d' | ||
+ | |||
+ | Par ailleurs, il faut retrouver les requêtes complètes pour les exécuter, car elles peuvent être présentées en plusieurs lignes. Il y a une méthode intéressante pour ça, qui s' | ||
+ | |||
+ | Enfin, puisque c'est un test de déverminage, | ||
<code python> | <code python> | ||
- | yield(u'{0};'.format(sql)) | + | # |
+ | # -*- coding: utf-8 -*- | ||
+ | # python v2.7 | ||
+ | |||
+ | import os | ||
+ | import codecs | ||
+ | import sqlite3 | ||
+ | |||
+ | from sqlite3dump import _iterdump as iterdump | ||
+ | |||
+ | ############################################################################# | ||
+ | def script2base_test(script, base=": | ||
+ | """ | ||
+ | | ||
+ | base: la base de données (nom du fichier avec son chemin) | ||
+ | | ||
+ | """ | ||
+ | |||
+ | # supprime la base si elle existe déjà et s'il s'agit d'un fichier disque | ||
+ | if base!=": | ||
+ | os.remove(base) | ||
+ | |||
+ | # ouvre la base de données sqlite3 | ||
+ | try: | ||
+ | cnx = sqlite3.connect(base) | ||
+ | cnx.execute(" | ||
+ | cur = cnx.cursor() | ||
+ | except sqlite3.Error, | ||
+ | print u"Echec pour la connexion à la base de données\n" | ||
+ | return | ||
+ | |||
+ | # lit et charge en mémoire le script SQL | ||
+ | with codecs.open(script, | ||
+ | scriptsql = f.readlines() | ||
+ | nbl = len(scriptsql) # nombre de lignes du script SQL | ||
+ | |||
+ | # lit et exécute le script SQL, requête par requête | ||
+ | i = 0 # compteur de lignes | ||
+ | r = 0 # compteur de requêtes | ||
+ | c = 0 # compteur d'erreurs | ||
+ | while i<nbl: | ||
+ | buffer = scriptsql[i] | ||
+ | while not sqlite3.complete_statement(buffer.encode(' | ||
+ | # NB: avec python 2, " | ||
+ | i += 1 | ||
+ | buffer += scriptsql[i] | ||
+ | |||
+ | if buffer.strip() not in [u" | ||
+ | try: | ||
+ | cur.execute(buffer) | ||
+ | cnx.commit() | ||
+ | except sqlite3.Error, | ||
+ | cnx.rollback() | ||
+ | c += 1 | ||
+ | print u"num ligne: %d; num requête: %d; Erreur: %s; requête: \n%s" % (i, r, err.args[0], | ||
+ | |||
+ | i += 1 # nouvelle ligne attendue | ||
+ | r += 1 # nouvelle requête attendue | ||
+ | |||
+ | # fermeture et effacement de la base temporaire | ||
+ | cur.close() | ||
+ | cnx.close() | ||
+ | |||
+ | # message de fin | ||
+ | print u" | ||
</ | </ | ||
- | Voilà: Après ces corrections, la méthode connexion.iterdump() ne posera plus de problème, même avec le traitement par cx_freeze! | + | Exemple d' |
+ | |||
+ | <code python> | ||
+ | # test d' | ||
+ | script = " | ||
+ | script2base_test(script) | ||
+ | </ | ||
+ | |||
+ | Si, par exemple, la requête numéro 805 (ligne 884) déclenche une erreur, voilà le message affiché: | ||
+ | |||
+ | < | ||
+ | num ligne: 884; num requête: 805; Erreur: foreign key constraint failed; requête: | ||
+ | INSERT INTO " | ||
+ | </ | ||
+ | |||
+ | Il y a une faute de frappe sur le nom de pays (" | ||