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, pour les projets en Delphi 6, 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.
La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal,
L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La
Notation Alsacienne
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
blogs ou 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 le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) 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, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |