menu
  Home  ==>  articles  ==>  bdd  ==>  interbase  ==>  sql_script_executer   

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 terminateur initial est ";"

     
    /* Table: PORTAL_ROLES, Owner: SYSDBA */

    CREATE TABLE "PORTAL_ROLES
    (
      "ROLEID" INTEGER NOT NULL,
      "PORTALID" INTEGER NOT NULL,
      "ROLENAME" VARCHAR(50),
     PRIMARY KEY ("ROLEID")
    );

    /* Table: PORTAL_USERS, Owner: SYSDBA */

    CREATE TABLE "PORTAL_USERS
    (
      "USERID" INTEGER NOT NULL,
      "NAME" VARCHAR(50),
      "APASSWORD" VARCHAR(50) NOT NULL,
      "EMAIL" VARCHAR(100) NOT NULL,
     PRIMARY KEY ("USERID"),
    CONSTRAINT "K_PORTAL_USERS_MAILUNIQUE ("EMAIL")
    );

    Ici les deux créations de table sont terminées par ";"

  • comme certaines procédures cataloguées contiennent aussi des ";", la convention courante (en tout cas celle d'IbConsole) est de changer temporairement de terminateur en utilisant la pseudo instruction SET TERM (SET TERMinator)

     
    /* generators */

    CREATE GENERATOR "USER_ITEMID";
    SET TERM ^ ;

    /* Stored procedures */

    CREATE PROCEDURE "CLEANUP
    AS
    BEGIN EXITEND ^

    CREATE PROCEDURE "PORTAL_GETSINGLEROLE
    (
      "ROLEIDINTEGER
    )
    RETURNS
    (
      "ROLENAMEVARCHAR(50)
    )
    AS
    BEGIN EXITEND ^

    Notez que

    • SET TERM se termine (encore) par ";"
    • les deux requêtes qui suivent comportent les ";" des procédures cataloguées, mais ces requêtes sont terminées à présent par "^"


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_symbole_integer_symbole_operator_symbol,
                      e_string_litteral_symbol,
                      e_comment_symbol,
                      e_end_symbol);

    c_ib_scriptclass(c_text_file)
                  m_symbol_typet_symbol_type;
                  m_blank_stringm_symbol_stringString;
                  _m_previous_indexInteger;

                  m_c_result_listtStringList;
                  m_request_indexInteger;

                  m_is_SET_TERMm_is_COMMIT_WORKm_is_SET_AUTODDLBoolean;
                  m_terminatorChar;

                  Constructor create_ib_script(p_namep_file_nameString);

                  function f_initializedBoolean;
                  procedure initialize_scanner;
                  function f_read_symbolBoolean;
                  procedure test_scanner;

                  function f_remove_commentString;

                  procedure initialize_request_scanner;
                  function f_next_requestString;

                  function f_get_requestString;

                  Destructor DestroyOverride;
                end// c_ib_script

  • puis la fonction qui récupère une requête:

    function c_ib_script.f_get_requestString;
      var l_requestString;

      // -- 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_typee_end_symbol
            or (m_symbol_type<> e_comment_symbol);

        if m_symbol_typee_end_symbol
          then l_request:= '';
        Result:= l_request;
      end// f_get_request

  • et cette fonction appelle:

    procedure get_request;
      var l_previous_identifierString;

      function f_is_SET_TERMBoolean;
        begin
          Result:=   (l_previous_identifier'SET')
                    and
                     (m_symbol_string'TERM')
        end// f_is_SET_TERM

      function f_is_COMMIT_WORKBoolean;
        begin
          Result:=   (l_previous_identifier'COMMIT')
                    and
                     (m_symbol_string'WORK')
        end// f_is_COMMIT_WORK

      function f_is_SET_AUTODDLBoolean;
        begin
          Result:=   (l_previous_identifier'SET')
                    and
                     (m_symbol_string'AUTODDL')
        end// f_is_SET_AUTODDL

      var l_displayBoolean;

      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_stringm_terminator
                  then begin
                      Break;
                    end
                  else l_request:= l_requestm_blank_stringm_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_typee_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(SenderTObject);

  // -- here execute_request()

  var l_requestString;
      l_trimmed_requestString;

  begin // execute_script_Click
    original_memo_.Lines.LoadFromFile(g_script_pathg_script_file_name);

    if not FileExists(g_script_pathg_script_file_name)
      then display_bug_stop('not_found 'g_script_pathg_script_file_name);

    connect_database_Click(Nil);

    with c_ib_script.create_ib_script('ib_script',
        g_script_pathg_script_file_namedo
    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_pathg_script_file_name)  ;
    end// with c_ib_script
  end// execute_script_Click

et chaque requête est lancée par:

procedure execute_request(p_requestString);
  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 eException 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:

image



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" :
    Nom :
    E-mail :
    Commentaires * :
     

  • 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.
Créé: oct-06. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
      + programmation_oracle
      + interbase
        – interbase_blobs
        – interbase_tutorial
        – interbase_dbexpress
        – interbase_ibx_net
        – ib_dbexpress_net
        – delphi_8_ado_net
        – borland_data_provider
        – sql_script_extraction
        – interbase_udf
        – sql_script_executer
        – ib_blob_extraction
        – insert_blob_script
        – ib_stored_procedures
      + sql_server
      + firebird
      + mysql
      + xml
      – paradox_via_ado
      – mastapp
      – delphi_business_objects
      – clientdataset_xml
      – data_extractor
      – rave_report_tutorial
      – visual_livebindings
      – migration_bde
    + web_internet_sockets
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Prestataire Delphi réalisation de projets Delphi, création de composants, migration Delphi - Tél 01.42.83.69.36