|
SQL Script Executer - John COLIBRI.
|
- résumé : un utilitaire qui permet d'exécuteur des scripts Sql Interbase
- mots clé : SQL Script - Interbase - exécution de script - script parser
- logiciel utilisé : Windows XP personnel, Delphi 6.0, Interbase 6/7
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque
dur
- champ d'application : Delphi 1 à 2006, Turbo Delphi sur Windows
- niveau : développeur Delphi / Interbase
- plan :
1 - Introduction
Un exécuteur de script SQL est un programme qui permet d'exécuter une ou
plusieurs requêtes SQL provenant d'un fichier ASCII.
Actuellement, les composants d'accès aux moteurs Sql gérés par Delphi ne
permettent d'exécuter qu'une seule requête à la fois. Cela convient
parfaitement pour récupérer par un seul SELECT toute une Table, ou pour
modifier des lignes par un seul UPDATE. Mais lorsqu'il s'agit de créer
une nouvelle base, avec création de Tables, procédures cataloguées, Triggers,
et remplissage initial des Tables, il est plus efficace de recourir à des
scripts.
De nombreux outils, dont IbConsole qui est fourni avec Delphi, permettent de
lancer des scripts.
Nous préférons utiliser notre propre mécanique, et pour deux raisons au moins
- la plupart des outils de gestion de base de données nécessitent la frappe
clavier de nombreux paramètres: nom d'utilisateur, mot de passe, nom du
fichier contenant le script etc. Si tout se pass bien, à la rigueur. Mais si
le script contient des erreurs, il faut morceler le texte en plusieurs
parties, lancer ces parties les unes après les autres, recommencer après
avoir corrigé les erreurs du script. Cela devient assez vite fatiguant
- certains de nos clients doivent pouvoir initialiser une base (nouvel
exercice, ajout d'un établissement etc). Nous préférons leur fournir un
logiciel Delphi qui s'en charge, plutôt que de leur demander de lancer
IbConsole puis cliquer ici ou là
2 - Principe
Le texte du script contient les requêtes les unes après les autres, avec un
terminateur quelconque après chaque requête.
En ce qui concerne Interbase:
Le travail à effectuer est alors assez simple:
- supprimer les commentaires (textes entre /* et */)
- débiter le texte en requêtes en analysant ce qui se trouve terminé par le
terminateur courant (";" au départ, puis la valeur indiquée par SET
TERM par la suite)
Il suffit donc d'analyser le script et de lancer chaque requête détectée.
3 - Le Projet Delphi
Le travail de l'analyseur
Il s'agit là d'une version de plus du scanner de base, qui détecte les
commentaires de script, et les mots clés qui nous intéressent: SET TERM .
Nous avons aussi découvert que d'autres pseudo instructions devaient être
traitées (éliminées, dans notre cas):
- SET SQL DIALECT
- COMMIT WORK
- SET AUTODDL OFF
Nous incluons donc ces mots clés dans l'analyseur.
L'analyseur de requête
Voici l'analyseur
- tout d'abord la définition de la CLASSe:
t_symbol_type= (e_unknown_symbol,
e_identifier_symbol, e_integer_symbol, e_operator_symbol,
e_string_litteral_symbol,
e_comment_symbol,
e_end_symbol);
c_ib_script= class(c_text_file)
m_symbol_type: t_symbol_type;
m_blank_string, m_symbol_string: String;
_m_previous_index: Integer;
m_c_result_list: tStringList;
m_request_index: Integer;
m_is_SET_TERM, m_is_COMMIT_WORK, m_is_SET_AUTODDL: Boolean;
m_terminator: Char;
Constructor create_ib_script(p_name, p_file_name: String);
function f_initialized: Boolean;
procedure initialize_scanner;
function f_read_symbol: Boolean;
procedure test_scanner;
function f_remove_comment: String;
procedure initialize_request_scanner;
function f_next_request: String;
function f_get_request: String;
Destructor Destroy; Override;
end; // c_ib_script
|
- puis la fonction qui récupère une requête:
function c_ib_script.f_get_request: String;
var l_request: String;
// -- here get_request()
begin // f_get_request
m_is_SET_TERM:= False;
m_is_COMMIT_WORK:= False;
m_is_SET_AUTODDL:= False;
repeat
get_request;
until (m_symbol_type= e_end_symbol)
or (m_symbol_type<> e_comment_symbol);
if m_symbol_type= e_end_symbol
then l_request:= '';
Result:= l_request;
end; // f_get_request
|
- et cette fonction appelle:
procedure get_request;
var l_previous_identifier: String;
function f_is_SET_TERM: Boolean;
begin
Result:= (l_previous_identifier= 'SET')
and
(m_symbol_string= 'TERM')
end; // f_is_SET_TERM
function f_is_COMMIT_WORK: Boolean;
begin
Result:= (l_previous_identifier= 'COMMIT')
and
(m_symbol_string= 'WORK')
end; // f_is_COMMIT_WORK
function f_is_SET_AUTODDL: Boolean;
begin
Result:= (l_previous_identifier= 'SET')
and
(m_symbol_string= 'AUTODDL')
end; // f_is_SET_AUTODDL
var l_display: Boolean;
begin // get_request
l_request:= '';
l_display:= False;
// -- break if found m_terminator
repeat
f_read_symbol;
if l_display
then display(Format('%7d ', [m_buffer_index])+ m_symbol_string);
if m_symbol_type<> e_comment_symbol
then begin
if f_is_SET_TERM
then begin
// -- get the terminator
f_read_symbol;
m_terminator:= m_symbol_string[1];
display('// -- SET TERM '+ m_symbol_string);
m_is_SET_TERM:= True;
// -- skip actual terminator and exit loop
f_read_symbol;
break;
end else
if f_is_COMMIT_WORK
then begin
m_is_COMMIT_WORK:= True;
end else
if f_is_SET_AUTODDL
then begin
// -- skip "OFF ;"
f_read_symbol;
m_is_SET_AUTODDL:= True;
end else
if m_symbol_string= m_terminator
then begin
Break;
end
else l_request:= l_request+ m_blank_string+ m_symbol_string;
l_previous_identifier:= m_symbol_string;
end;
if m_buffer_index= _m_previous_index
then begin
display_bug_stop('no_progress');
Break;
end;
_m_previous_index:= m_buffer_index;
until m_symbol_type= e_end_symbol;
end; // get_request
|
Lancer les requêtes
Le projet principal charge le texte du script et récupère les requêtes
procedure TForm1.execute_script_Click(Sender: TObject);
// -- here execute_request()
var l_request: String;
l_trimmed_request: String;
begin // execute_script_Click
original_memo_.Lines.LoadFromFile(g_script_path+ g_script_file_name);
if not FileExists(g_script_path+ g_script_file_name)
then display_bug_stop('not_found '+ g_script_path+ g_script_file_name);
connect_database_Click(Nil);
with c_ib_script.create_ib_script('ib_script',
g_script_path+ g_script_file_name) do
begin
if f_initialized
then begin
initialize_scanner;
repeat
l_request:= f_get_request;
if not (m_is_SET_TERM or m_is_COMMIT_WORK or m_is_SET_AUTODDL)
then begin
l_trimmed_request:= f_add_return_line_feed(l_request);
l_trimmed_request:= f_change_returns_in_spaces(l_trimmed_request);
l_trimmed_request:= Trim(f_remove_double_spaces(l_trimmed_request));
if exec_.Checked
then begin
if (Pos('DIALECT', l_request)<= 0)
and
(Trim(l_request)<> '')
then
execute_request(l_request)
end
else display(l_request);
end;
until l_request= '';
end
else display_bug_stop('not_found '+ g_script_path+ g_script_file_name) ;
end; // with c_ib_script
end; // execute_script_Click
|
et chaque requête est lancée par:
procedure execute_request(p_request: String);
begin
try
if IbDatabase1.DefaultTransaction.InTransaction
then IbDatabase1.DefaultTransaction.Commit;
IbDatabase1.DefaultTransaction.StartTransaction;
IbSql1.Sql.Text:= p_request;
// -- for stored procedures
IbSql1.ParamCheck:= False;
IbSql1.ExecQuery;
IbDatabase1.DefaultTransaction.Commit;
except
on e: Exception do
begin
display_bug_stop('*** pb_'+ e.Message);
end;
end; // try except
end; // execute_request
|
Min manuel
Le script
Nous avons choisi de recréer la base MASTAPP.
Le script est obtenu en utilisant l'extracteur de
script. Le script est contenu dans le fichier schema_IB_MASTSQL_6.txt
(dans le .ZIP téléchargeable ci-dessous)
HowTo
Pour utiliser le logiciel
|
compilez le projet
|
|
sélectionnez le répertoire et la base à l'aide du tDirectoryListBox de
l'onglet "database". Puis
- si la base existe sélectionnez le fichier
- sinon, créez la base en cliquant "create_base_" dans l'onglet "script_"
|
|
sélectionnez le script qui vous intéresse en utilisant le
tDirectoryListBox et le tFileListBox de l'onglet "script_"
|
|
sélectionnez "exec_" puis cliquez "execute_script_"
|
Voici l'image du projet:
Quelques soucis
Parmi les petites misères:
- il a fallu veiller à restaurer les "Return LineFeed" complets (certains
scripts ne contenaient, ici ou là, que des LineFeed UNIX
- les valeurs littérales des dates doivent être à un format commestible pour
le moteur. Donc soit "mois, jour, année", soit, et c'est nouveau pour nous,
"année, mois, jour"
- les guillemets sont placés dans le script du générateur de script, mais
doivent être retirés pour l'exécution des scripts
- les blobs doivent faire l'objet d'un traitement séparé (mettant en oeuvre
des tStreams)
En étant un rien cynique, nous pourrions récupérer les sources de IbConsole,
qui sait bien franchir toutes ces barrières sans souci. En attendant, cet
utilitaire a rempli les missions que nous avions envisagées.
5 - Télécharger le code source Delphi
Vous pouvez télécharger:
- script_executer.zip : le projet avec l'analyseur de script, et le texte
du script MASTAPP (37 K)
Ce .ZIP qui comprend:
- le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque
.ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent:
- créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre,
de DLL ou autre). Pour supprimer le projet, effacez le répertoire.
Comme d'habitude:
- nous vous remercions de nous signaler toute erreur, inexactitude ou
problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion
d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre
e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos réponses
sur les messageries. C'est très simple: plus nous aurons de visiteurs et de
références Google, plus nous écrirons d'articles.
6 - L'auteur
John COLIBRI est passionné par le développement
Delphi et les applications de Bases de Données. Il a écrit de nombreux livres
et articles, et partage son temps entre la
développement de projets pour
ses clients, le conseil et la formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des
stages
de formation Delphi, base de données, Ado.Net, Asp.Net et UML qu'il
anime personellement tous les mois, à Paris, en province ou sur site client.
|