#!/usr/bin/perl -w package sybase2postgresql; #------------------------------------------------------------------------------ # Project : Sybase to PostgreSQL database converter # Name : sybase2postrgresql #------------------------------------------------------------------------------ use strict; # impose la déclaration explicite des variables use Getopt::Std; use AppConfig qw(:expand :argcount); =head1 NOM Convertisseur de base de donnee Sybase vers Postgresql =head1 SYNOPSIS Outils permettant de transformer (a partir de fichier scripts SQL) une base Sybase vers une base PostgreSQL. =over 4 =item * Pour voir les options disponibles, tapper sybase2postgresql -h =item * La plupart des options permettent d'indiquer l'emplacement de scripts SQL qui seront transformes pour etre utilisables par PostgreSQL. =item * A partir du ficher SQL de creation des tables, sybase2postregresql va produire les scripts de creation des tables, des commandes pour bcp (recuperation des donnees), de mise a jour des sequences (pour que l'incerementation automatique reprenne apres le dernier enregistrement cree) qui devra etre execute apres l'insertion des donnees dans la base =item * Le programme peut être appele en lignes de commandes avec les options ou avec un fichier de configuration =item * L'ensemble des operations peut etre automatise grace a l'option -a =back =head1 DESCRIPTION =cut my $debug=vrai(); my %opts; my $bdd=""; my $server=""; my $user=""; my $passwd=""; my $freebcp=faux(); my $fichiers_out = "fichiers_traites.log"; open(FICHIERS_TRAITES, ">$fichiers_out") or die ("impossible d'ecrire dans le fichier : $fichiers_out \n"); getopts('ha:b:t:i:v:g:d:f:S:U:P:c:', \%opts); print("Transformation d'une base Sybase vers PostgreSQL\n"); if($opts{c}){ print "Utilisation du fichier de configuration : ". $opts{c} ."\n"; configuration($opts{c}); } else { if($opts{a}){ $freebcp= vrai(); } if($opts{b}){ $bdd= $opts{b}; print("Traitement sur la base de donnees : ".$bdd."\n\n"); } if($opts{S}){ $server= $opts{S}; } if($opts{U}){ $user= $opts{U}; } if($opts{P}){ $passwd= $opts{P}; } if($opts{t}){ if ($opts{b} && $opts{S} && $opts{U} && $opts{P}){ traite_table($opts{t}); } else { print(" Vous devez preciser le nom de la base de donnees, le serveur, l'utilisateur et le mot de passe Sybase\n"); } } if($opts{i}){ traite_index($opts{i}); } if($opts{v}){ if ($opts{b}){ traite_vue($opts{v}); } else { print(" Vous devez preciser le nom de la base de donnees\n"); } } if($opts{g}){ traite_trigger($opts{g}); } if($opts{d}){ traite_donnee($opts{d}); } if($opts{f}){ traite_procedure($opts{f}); } close(FICHIERS_TRAITES); if($opts{a}){ automatique(); } } if($opts{h}){ print("Usage : sybase2postgresql [-h] [-a] [-b nom_base] [-t fichier_table] [-i fichier_index] [-v fichier_vue] [-g fichier_trigger] [-d fichier_donnees] [-f fichier_fonction] [-S serveur_sybase] [-U utilisateur_sybase] [-P mot_de_passe_sybase] [-c fichier_configuration]\n\n"); print(" -h affiche cette aide\n"); print(" -a si les options b, t, i, v, g, p, S, U, P, sont definies (ou si le fichier de configuration contient les informations), tout le traitement peut etre automatique (il n'est pas nécessaire de donner de nom de fichier pour les donnees car celui-ci est genere automatiquement)\n"); print(" -b nom de la base de donnees sur laquelle porte la transformation\n"); print(" -t fichier contenant la description des tables de la base\n"); print(" -i fichier contenant la description des index de la base\n"); print(" -v fichier contenant la description des vues de la base\n"); print(" -g fichier contenant la description des triggers de la base\n"); print(" -d fichier contenant les donnees d'une table\n"); print(" -f fichier contenant les fonctions (procedures) d'une base\n"); print(" -S nom (ou adresse) du serveur Sybase de la base origine\n"); print(" -U nom d'utilisateur pour la base Sybase\n"); print(" -P mot de passe pour la base Sybase\n"); print(" -c fichier de configuration contenant les options : permet de ne pas specifier chacune des options ci dessus\n"); } =pod =head2 Les Methodes "outils" =head3 configuration Va lire le fichier de configuration passe en parametre et initialise les variables en fonction de son contenu. =cut sub configuration { # on récupère la ligne passée en parametre my ($fichier) = @_; my $config = AppConfig->new( 'a', 'b' => {ARGCOUNT => 1}, 'S' => {ARGCOUNT => 1}, 'U' => {ARGCOUNT => 1}, 'P' => {ARGCOUNT => 1}, 't' => {ARGCOUNT => ARGCOUNT_LIST}, 'i' => {ARGCOUNT => ARGCOUNT_LIST}, 'v' => {ARGCOUNT => ARGCOUNT_LIST}, 'g' => {ARGCOUNT => ARGCOUNT_LIST}, 'd' => {ARGCOUNT => ARGCOUNT_LIST}, 'f' => {ARGCOUNT => ARGCOUNT_LIST}); $config->file($fichier); if (defined($config->a())){ $freebcp=vrai(); } if (defined($config->b())){ $bdd= $config->b(); print("Traitement sur la base de donnees : ".$bdd."\n"); } if (defined($config->S())){ $server= $config->S(); print "Utilisation du serveur Sybase : ". $server ."\n"; } if (defined($config->U())){ $user= $config->U(); print "Utilisateur Sybase : ", $user, "\n"; } if (defined($config->P())){ $passwd= $config->P(); print "Mot de passe Sybase : ", $passwd, "\n"; } if (defined($config->t())){ my $t = $config->t(); foreach my $k(@$t){ traite_table($k); } } if (defined($config->i())){ my $i = $config->i(); foreach my $k(@$i){ traite_index($k); } } if (defined($config->v())){ my $v = $config->v(); foreach my $k(@$v){ traite_vue($k); } } if (defined($config->g())){ my $g = $config->g(); foreach my $k(@$g){ traite_trigger($k) } } if (defined($config->d())){ my $d = $config->d(); foreach my $k(@$d){ traite_donnee($k); } } if (defined($config->f())){ my $f = $config->f(); foreach my $k(@$f){ traite_procedure($k); } } close(FICHIERS_TRAITES); if (defined($config->a())){ automatique(); } } =pod =head3 automatique Automatise le fonctionnement du script : toutes les actions sont effectuees automatiquement (a condition que tous les renseignements necessaires soient fournis) =cut sub automatique { my $fichiers_traites = AppConfig->new( 'table' => {ARGCOUNT => ARGCOUNT_LIST}, 'bcp' => {ARGCOUNT => ARGCOUNT_LIST}, 'bcp_out' => {ARGCOUNT => ARGCOUNT_LIST}, 'sequence' => {ARGCOUNT => ARGCOUNT_LIST}, 'index' => {ARGCOUNT => ARGCOUNT_LIST}, 'vue' => {ARGCOUNT => ARGCOUNT_LIST}, 'trigger' => {ARGCOUNT => ARGCOUNT_LIST}, 'donnee' => {ARGCOUNT => ARGCOUNT_LIST}, 'fonction' => {ARGCOUNT => ARGCOUNT_LIST}); $fichiers_traites->file($fichiers_out); print " ===============================\n||Debut de la phase automatisee||\n ===============================\n"; # Création de la base de données my $commande = "createdb -E LATIN1 ".$bdd; print "Creation de la base de donnees : \n--------------------------------\n".$commande."\n"; my $retour = `$commande`; print $retour."\n"; # Applique les Patch pour la base de données my $cmd_pl = "createlang plpgsql ".$bdd; my $ret_pl = `$cmd_pl`; my $cmd_patch = "psql -c \'\\i PATCH_pour_postgreSQL.txt\' ".$bdd; print $cmd_patch."\n"; my $ret_patch = `$cmd_patch`; print $ret_patch."\n"; # Création des tables dans la base if (defined($fichiers_traites->table())){ print "Creation des tables dans la base :\n----------------------------------\n"; my $table = $fichiers_traites->table(); foreach my $element(@$table){ my $cmd = "psql -c \'\\i ".$element."\' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $ret."\n"; } } # Création des indexs dans la base if (defined($fichiers_traites->index())){ print "Creation des indexs dans la base :\n----------------------------------\n"; my $liste = $fichiers_traites->index(); foreach my $element(@$liste){ my $cmd = "psql -c \'\\i ".$element."\' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $ret."\n"; } } # Execution de la commande bcp if (defined($fichiers_traites->bcp())){ print "Recuperation des donnees de Sybase :\n------------------------------------\n"; my $liste = $fichiers_traites->bcp(); foreach my $element(@$liste){ print $element."\n"; open(FICHIER, "<$element") or die ("le fichier $element ne peut pas etre ouvert\n"); while (defined(my $ligne = )){ print $ligne."\n"; my $ret = `$ligne`; print $ret."\n"; } } } # Traitement des données pour les rendre intégrables if (defined($fichiers_traites->bcp_out())){ print "Traitement des donnees pour les rendre integrables :\n----------------------------------\n"; my $liste = $fichiers_traites->bcp_out(); foreach my $element(@$liste){ open(FICHIER, "<$element") or die ("le fichier $element ne peut pas etre ouvert\n"); while (defined(my $ligne = )){ traite_donnee($ligne); } } } # Integration des données à la base if (defined($fichiers_traites->donnee())){ print "Integration des donnees a la base :\n-----------------------------------\n"; my $liste = $fichiers_traites->donnee(); foreach my $element(@$liste){ if($element =~ m/(.*\/)([^\/]+)\.bcp$/){ my $cmd = "psql -c \'COPY ".$1." FROM \"".$element."\" USING DELIMITERS \'|\' \' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $ret."\n"; } else { print "ERREUR lors de l'ingration des donnees"; } } } # Mise à jour des séquences if (defined($fichiers_traites->sequence())){ print "Mise a jour des sequences dans la base :\n----------------------------------------\n"; my $liste = $fichiers_traites->sequence(); foreach my $element(@$liste){ my $cmd = "psql -c \'\\i ".$element."\' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $ret."\n"; } } # Création des trigger if (defined($fichiers_traites->trigger())){ print "Creation des triggers dans la base :\n------------------------------------\n"; my $liste = $fichiers_traites->trigger(); foreach my $element(@$liste){ my $cmd = "psql -c \'\\i ".$element."\' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $ret."\n"; } } # Création des procédures if (defined($fichiers_traites->fonction())){ print "Creation des fonctions dans la base :\n-------------------------------------\n"; my $liste = $fichiers_traites->fonction(); foreach my $element(@$liste){ my $cmd = "psql -c \'\\i ".$element."\' ".$bdd; print $cmd."\n"; my $ret = `$cmd`; print $retour."\n"; } } } =pod =head3 clear_line Netoie une ligne passee en parametres en enlevant le retour chariot, remplacant les lettres accentuees par des lettres "normales" et en passant tout le texte en minuscules. =cut sub clear_line { # on récupère la ligne passée en parametre my ($ligne) = @_; # en enleve le retour chariot chomp $ligne; # on remplace les caracteres accentues par les caracteres normaux correspondants $ligne =~ tr/\300\302\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374°/AACEEEEIIOUUUaaceeeeiiouuuo/; # on passe toute la ligne en minuscule $ligne = "\L$ligne\E "; # on retourne la ligne return $ligne; } =pod =head3 convert2cast Transforme les fonction convert(type, variable) en fonction CAST(variable AS type) =cut sub convert2cast { # on récupère la ligne passée en parametre my ($ligne) = @_; my $pe; $pe = qr{\((?:(?>[^()]+)|(??{ $pe }))*\)}x; my $motif = qr/convert$pe/; while ($ligne =~ m/($motif)/) { my $old = $1; my $new = $old; $new =~ s/convert\(\s*([^,]+?)\s*,\s*(.+?)\s*\)$/CAST($2 AS $1)/; $ligne =~ s/\Q$old\E/$new/; } # on retourne la ligne return $ligne; } =pod =head3 right2lpad Transforme les fonction right('remplissage' + chaine, longueur) en fonction lpad(chaine, longueur, 'remplissage') =cut sub right2lpad { # on récupère la ligne passée en parametre my ($ligne) = @_; my $pe; $pe = qr{\((?:(?>[^()]+)|(??{ $pe }))*\)}x; my $motif = qr/right$pe/; $ligne =~ s/right\s*\(/right\(/g; while ($ligne =~ m/($motif)/) { my $old = $1; my $new = $old; $new =~ s/right\(\s*\'([^\'])[^\']*\'\s*\+\s*(.+?)\s*,(\d+)\)$/lpad\($2, $3, \'$1\'\)/; $ligne =~ s/\Q$old\E/$new/; } # on retourne la ligne return $ligne; } =pod =head3 faux Permet de palier au manque de booleen (renvoie 0) =cut sub faux { return 0; } =pod =head3 vrai Permet de palier au manque de booleen (renvoie 1) =cut sub vrai { return 1; } =pod =head3 decommente_fichier Permet d'enlever les commentaires d'un fichier et de remettre les fonctions (detectees par leurs parentheses) sur une seule ligne =cut sub decommente_fichier { my ($parametre) = @_; my $dest=""; # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1temp_$2"; } else{ $dest = "temp_".$parametre; } # ouvrir les fichiers necessaires open(FICHIER_SOURCE, "<$parametre") or die ("le fichier $parametre ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); my $dans_commentaire = faux(); my $dans_fonction = faux(); my $parentheses_ouvertes = 0; # nombre de parentheses ouvertes my $fonction=""; # fonction en cours my $commentaire=""; # ligne comportant des commantaires non fermés # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide, on la traite if ($ligne !~ m/^\s*$/){ 1 while $ligne =~ s/\/\*.*\*\///; # supprime les commentaires contenus uniquement sur la ligne (repérés par /* */ $ligne =~ s/--.*//; # supprime tout ce qui suit un double tiret (commentaire à partir de ce point jusqu'à la fin de la ligne # si la ligne commence par un select, on saute une ligne après lui $ligne =~ s/^\s*select /select \n/; # s'il reste encore un /* if ($ligne =~ m/\/\*/ && $dans_commentaire == faux()){ chomp $ligne; $commentaire = $ligne; $dans_commentaire = vrai(); } elsif (($ligne =~ m/\(/ || $ligne =~ m/\+\s*$/)&& $dans_fonction == faux()){ chomp $ligne; my $ouvre = "("; my $ferme = ")"; ++$parentheses_ouvertes while ($ligne =~ m/[$ouvre]/g); --$parentheses_ouvertes while ($ligne =~ m/[$ferme]/g); if ($parentheses_ouvertes>0 || $ligne =~ m/\+\s*$/){ $fonction = $ligne; $dans_fonction = vrai(); } else { print FICHIER_DESTINATION $ligne."\n"; } } else{ if ($dans_commentaire == vrai()){ chomp $ligne; $commentaire .= $ligne; 1 while $commentaire =~ s/\/\*.*\*\///; if ($commentaire !~ m/\/\*/){ print FICHIER_DESTINATION $commentaire; $dans_commentaire = faux(); } } elsif ($dans_fonction == vrai()){ chomp $ligne; $fonction .= $ligne; my $ouvre = "("; my $ferme = ")"; ++$parentheses_ouvertes while ($ligne =~ m/[$ouvre]/g); --$parentheses_ouvertes while ($ligne =~ m/[$ferme]/g); if ($parentheses_ouvertes==0 && $ligne !~ m/\+\s*$/){ print FICHIER_DESTINATION $fonction."\n"; $dans_fonction=faux(); } } else { print FICHIER_DESTINATION $ligne; } } } } close(FICHIER_SOURCE); close(FICHIER_DESTINATION); return $dest; } =pod =head2 Les Methodes "de transformation" =head3 traite_table Traite le fichier de script SQL de creation des tables. =cut sub traite_table { my ($parametre) = @_; my $dest=""; # fichier de creation des tables my $bcp=""; # fichier de lancement des commandes bcp my $bcp_out=""; # fichier contenant le nom des fichiers bcp qui vont être crées my $sequence=""; # fichier de mise a jour des numero de sequence # modifie le nom du fichier source pour avoir le nom des fichiers destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = $1."postgres_".$2; $bcp = $1."bcp_".$2; $bcp_out = "bcp_out_".$2; $sequence = $1."postgres_sequence_".$2; } else{ $dest = "postgres_$parametre"; $bcp = "bcp_$parametre"; $bcp_out = "bcp_out_$parametre"; $sequence = "postgres_sequence_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers les fichiers \n"; print FICHIERS_TRAITES "table = ".$dest."\n"; print FICHIERS_TRAITES "bcp = ".$bcp."\n"; print FICHIERS_TRAITES "bcp_out = ".$bcp_out."\n"; print FICHIERS_TRAITES "sequence = ".$sequence."\n\n"; my $commandebcp=""; my $fincmdbcp=""; if ($freebcp == vrai()){ $commandebcp = "freebcp"; $fincmdbcp = "-c -t\"|\" -U".$user." -P".$passwd." -S".$server."\n"; } else { $commandebcp = "bcp"; $fincmdbcp = "-c -t\"|\" -Jiso_1 -U".$user." -P".$passwd." -S".$server."\n"; } print "Traitement des tables du fichier $parametre :"; # ouvrir le fichier contenant la structure des tables (passe en paramètre) open(FICHIER_SOURCE, "<".$parametre) or die ("le fichier $parametre ne peut pas etre ouvert\n"); # ouvre les fichiers destination open(FICHIER_DESTINATION, ">".$dest)or die ("le fichier $dest ne peut pas etre ouvert\n"); open(FICHIER_BCP, ">".$bcp) or die ("le fichier $bcp ne peut pas etre ouvert\n"); open(FICHIER_BCP_OUT, ">".$bcp_out) or die ("le fichier $bcp_out ne peut pas etre ouvert\n"); open(FICHIER_SEQUENCE, ">".$sequence) or die ("le fichier $sequence ne peut pas etre ouvert\n"); my $table=""; # contient les differentes lignes qui composent une commande de creation d'une table my $nom_table=""; # contient le nom de la table en cours de creation my $table_avec_sequence=faux(); # Indique si la table en cour d'analyse possède une séquence (et donc si la ligne pour bcp a deja ete cree. # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide (ou uniquement composee d'espaces , on la traite if ($ligne !~ m/^\s*$/){ # on nettoie la ligne $ligne = clear_line($ligne); # on remplace les types de donnees non valides pour postgreSQL $ligne =~ s/\sdatetime\s/ timestamp /; $ligne =~ s/\simage\s/ bytea /; $ligne =~ s/\sbinary\s/ bytea /; $ligne =~ s/\stinyint\s/ smallint /; # si la ligne contient un "create table", on garde le nom de la table if ($ligne =~ m/create table "(.+)"\s*$/){ $nom_table=$1; # $ligne =~ s/create table "(\S).+"\s/create table "$bdd.$1" /; # pour l'utilisation } # si la ligne contient un identity, on le remplace en creant une sequence if ($ligne =~ m/^\s*(\S+)\s+(.+\))\s+identity /){ # on recupere le nom de la colonne my $nom_col = $1; ### Traitement vers le fichier DESTINATION ### # on cree le nom de la sequence a partir du nom de table et de collonne my $nom_seq = $nom_table."_".$nom_col."_seq"; # on insere la commande de creation de la sequence print FICHIER_DESTINATION "create sequence ".$nom_seq.";\n"; # on recupere le type de donnee my $type=$2; # on remplace le type de donnee pour qu'il utilise la sequence $type =~ s/\(/\\\(/; $type =~ s/\)/\\\)/; $ligne =~ s/$type/integer/; $ligne =~ s/ identity / default nextval \(\'$nom_seq\') /; ### Mémorisation et ajout dans le fichier BCP ### $table_avec_sequence = vrai(); print FICHIER_BCP $commandebcp." ".$bdd."..".$nom_table." out ".$nom_table.".bcp -E ".$fincmdbcp; print FICHIER_BCP_OUT $nom_table.".bcp\n"; ### Ajout dans le fichier SEQUENCE ### print FICHIER_SEQUENCE "SELECT setval (\'".$nom_seq."\',(SELECT max(".$nom_col.") FROM ".$nom_table."));\n"; } # Si la ligne contient un ; (on est a la fin d'une commande), on ecrit la commande de creation de la table dans le fichier de destination if ($ligne =~ m/(.*;)(.*)/){ print FICHIER_DESTINATION $table; print FICHIER_DESTINATION "$1\n"; $table=$2; # si la commande bcp n'a pas deja ete cree, on le fait maintenant if ($table_avec_sequence == faux()){ print FICHIER_BCP $commandebcp." ".$bdd."..".$nom_table." out ".$nom_table.".bcp ".$fincmdbcp; print FICHIER_BCP_OUT $nom_table.".bcp\n"; } # on finit en réinitialisant la variable pour que la prochaine table soit sans séquence $table_avec_sequence=faux(); } # sinon on concatenne la ligne courrante a la commande de creation de table en cours else{ $table .= $ligne."\n"; } } } # on ecrit ce qui reste dans le script dans le fichier (normalement, il ne devrait rien rester ca toute commande est censee se terminer par un ;) print FICHIER_DESTINATION $table; # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_SEQUENCE); close(FICHIER_BCP); close(FICHIER_BCP_OUT); close(FICHIER_DESTINATION); print " OK\n"; } =pod =head3 traite_index Traite le fichier de script SQL de creation des index. =cut sub traite_index { my ($parametre) = @_; my $dest=""; # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1postgres_$2"; } else{ $dest = "postgres_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers le fichier :\n"; print FICHIERS_TRAITES "index = ".$dest."\n\n"; print "Traitement des indexes du fichier $parametre vers le fichier $dest : "; # ouvrir le fichier contenant la structure des tables (passe en paramètre) open(FICHIER_SOURCE, "<$parametre") or die ("le fichier $parametre ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); my $index=""; # contient les differentes lignes qui composent une commande de creation d'un index my $nom_index=""; # contient le nom de l'index pour pouvoir le modifier my $nom_table=""; # contient le nom de la table sur laquelle porte l'index # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide (ou uniquement composee d'espaces) et si elle n'est pas de type "on cci_data" ou "on cci_index" , on la traite if (($ligne !~ m/^\s*$/)&&($ligne !~ m/^\s*(on)\s*\w+(_data|_index)/)){ # on nettoie la ligne $ligne = clear_line($ligne); chomp $ligne; # on supprime les "unclustered" et les "clustered" $ligne =~ s/\snonclustered\s/ /; $ligne =~ s/\sclustered\s/ /; if ($ligne =~ m/create.*index\s*\"([^\"]*)\"/){ $nom_index=$1; } if ($ligne =~ m/on\s*\"([^\"]*)\"/){ $nom_table=$1; } # Si la ligne contient un ; (on est a la fin d'une commande), on ecrit la commande de creation de l'index dans le fichier de destination if ($ligne =~ m/(.*;)(.*)/){ my $temp = $index.$1."\n"; #print $temp."\n"; #print "index : ".$nom_index."\n"; #print "table : ".$nom_table."\n"; $index=$2; $temp =~ s/$nom_index/$nom_table\_$nom_index/; print FICHIER_DESTINATION $temp; $nom_index=""; $nom_table=""; } # sinon on concatenne la ligne courrante a la commande de creation d'index en cours else{ $index .= $ligne; } } } # on ecrit ce qui reste dans le script dans le fichier (normalement, il ne devrait rien rester a part des commentaires car toute commande est censee se terminer par un ;) print FICHIER_DESTINATION $index; # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_DESTINATION); print " OK\n"; } =pod =head3 traite_vue Traite le fichier de script SQL de creation des vues =cut sub traite_vue { my ($parametre) = @_; my $fichier_temp = decommente_fichier($parametre); my $dest=""; # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1postgres_$2"; } else{ $dest = "postgres_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers le fichier :\n"; print FICHIERS_TRAITES "vue = ".$dest."\n\n"; print "Traitement des vues du fichier $parametre vers le fichier $dest : "; # ouvrir les fichiers necessaires open(FICHIER_SOURCE, "<$fichier_temp") or die ("le fichier $fichier_temp ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); my $vue=""; # contient les differentes lignes qui composent une commande de creation d'une vue my $avant_from = vrai();#indicateur pour savoir si on se trouve avant le from my $fonction = faux(); #indique si on se trouve dans une fonction my $parentheses_ouvertes = 0; #nombre de parentheses non refermees my $fonction_en_cours = ""; #contenu de la fonction en cours my $nom_alias = ""; #alias a donner au champ # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide (ou uniquement composee d'espaces), on la traite if ($ligne !~ m/^\s*$/){ # on nettoie la ligne $ligne = clear_line($ligne); if ($ligne =~ m/\s*from\s+/){ $avant_from = faux(); $fonction = faux(); } # Remplacement du type datetime $ligne =~ s/\(datetime,/\(timestamp,/; # remplacement des fonctions $ligne =~ s/isnull/coalesce/g; $ligne =~ s/datepart\(year/date_part\(\'year\'/g; $ligne =~ s/datepart\(yy/date_part\(\'year\'/g; $ligne =~ s/datepart\(month/date_part\(\'month\'/g; $ligne =~ s/datepart\(mm/date_part\(\'month\'/g; $ligne =~ s/datepart\(hh/date_part\(\'hour\'/g; $ligne =~ s/datepart\(mi/date_part\(\'minute\'/g; $ligne =~ s/datename\(mm,/monthname\(/g; $ligne =~ s/convert\s+\(/convert\(/g; if ($ligne =~ m/convert/){ $ligne = convert2cast($ligne); } if ($ligne =~ m/right/){ $ligne = right2lpad($ligne); } if ($ligne =~ m/where hab_quota = 1/){ $ligne =~ s/1/CAST(1 AS bit)/; } # remplacement de ":" par ':' $ligne =~ s/\":\"/\':\'/; if ($avant_from == vrai()){ # remplacement des doubles guillements par 2 cotes $ligne =~ s/\"\"/\'\'/g; $ligne =~ s/\" \"/\' \'/g; # remplacement des + par des signes de concatenation $ligne =~ s/\+/\|\|/g; if ($fonction == faux()){ if ($ligne !~ m/\(/) { # echange des termes et remplacement du = par AS pour les alias de champ $ligne =~ s/\"([^\"]*)\"\s*=\s*([^,]*)/$2 AS $1/; $ligne =~ s/\'([^\']*)\'\s*=\s*([^,]*)/$2 AS $1/; $ligne =~ s/(.*)\s*=\s*([^,]*)/$2 AS $1/; } else { $fonction=vrai(); my $ouvre = "("; my $ferme = ")"; ++$parentheses_ouvertes while ($ligne =~ m/[$ouvre]/g); --$parentheses_ouvertes while ($ligne =~ m/[$ferme]/g); if ($ligne =~ m/\"([^\"]*)\"\s*=\s*(.*)/){ $fonction_en_cours = $2; $nom_alias = $1; } elsif ($ligne =~ m/\'([^\']*)\'\s*=\s*(.*)/){ $fonction_en_cours = $2; $nom_alias = $1; } elsif ($ligne =~ m/(.*)\s*=\s*(.*)/){ $fonction_en_cours = $2; $nom_alias = $1; } } } else { my $ouvre = "("; my $ferme = ")"; ++$parentheses_ouvertes while ($ligne =~ m/[$ouvre]/g); --$parentheses_ouvertes while ($ligne =~ m/[$ferme]/g); $fonction_en_cours .= $ligne; } } else { #remplacement des guillements par des cotes $ligne =~ s/\"/\'/g; } $ligne =~ s/$bdd\.\.//g; ################ ne sert qu'en mode debug #################### if ($debug==vrai()){ $ligne =~ s/\*=/=/g; $ligne =~ s/=\*/=/g; } ################################################# # Si la ligne contient un ; (on est a la fin d'une commande), on ecrit la commande de creation de l'index dans le fichier de destination if ($ligne =~ m/(.*;)(.*)/){ print FICHIER_DESTINATION $vue."$1\n"; $vue = $2; $avant_from = vrai(); } # sinon on concatenne la ligne courrante a la commande de creation d'index en cours else{ if ($fonction == faux()){ $vue .= $ligne."\n"; } elsif ($avant_from == faux()){ $vue .= $fonction_en_cours." AS ".$nom_alias."\n"; $fonction = faux(); } elsif ($parentheses_ouvertes == 0 && $fonction_en_cours =~ m/(.*),\s*$/){ #print $nom_alias." - ".$1."\n"; $vue .= $1." AS ".$nom_alias.",\n"; $fonction = faux(); } elsif ($parentheses_ouvertes == 0 && $fonction_en_cours =~ m/(.*[^,]{1})\s*$/){ $vue .= $1." AS ".$nom_alias."\n"; $fonction = faux(); } } } } # on ecrit ce qui reste dans le script dans le fichier (normalement, il ne devrait rien rester a part des commentaires car toute commande est censee se terminer par un ;) print FICHIER_DESTINATION $vue; # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_DESTINATION); print " OK\n"; } =pod =head3 traite_procedure Traite le fichier contenant les procedures afin de les rendre utilisables par PostgreSQL. =cut sub traite_procedure { my ($parametre) = @_; my $fichier_temp = decommente_fichier($parametre); my $dest=""; # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1postgres_$2"; } else{ $dest = "postgres_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers le fichier :\n"; print FICHIERS_TRAITES "fonction = ".$dest."\n\n"; print "Traitement des vues du fichier $parametre vers le fichier $dest : "; # ouvrir les fichiers necessaires open(FICHIER_SOURCE, "<$fichier_temp") or die ("le fichier $fichier_temp ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); my $procedure=""; # contient les differentes lignes qui composent une commande de creation d'une procedure # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide (ou uniquement composee d'espaces), on la traite if ($ligne !~ m/^\s*$/){ # on nettoie la ligne $ligne = clear_line($ligne); if ($ligne =~ m/(.*;)(.*)/){ print FICHIER_DESTINATION $procedure."$1\n"; $procedure = $2; } else { $procedure .= $ligne."\n"; } } } # on ecrit ce qui reste dans le script dans le fichier (normalement, il ne devrait rien rester a part des commentaires car toute commande est censee se terminer par un ;) print FICHIER_DESTINATION $procedure; # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_DESTINATION); print " OK\n"; } =pod =head3 traite_trigger Traite le fichier contenant les procedures afin de les rendre utilisables par PostgreSQL. =cut sub traite_trigger { my ($parametre) = @_; my $fichier_temp = decommente_fichier($parametre); my $dest=""; # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1postgres_$2"; } else{ $dest = "postgres_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers le fichier :\n"; print FICHIERS_TRAITES "trigger = ".$dest."\n\n"; print "Traitement des vues du fichier $parametre vers le fichier $dest : "; # ouvrir les fichiers necessaires open(FICHIER_SOURCE, "<$fichier_temp") or die ("le fichier $fichier_temp ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); my $trigger=""; # contient les differentes lignes qui composent une commande de creation d'un trigger # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # si la ligne n'est pas vide (ou uniquement composee d'espaces), on la traite if ($ligne !~ m/^\s*$/){ # on nettoie la ligne $ligne = clear_line($ligne); if ($ligne =~ m/(.*;)(.*)/){ print FICHIER_DESTINATION $trigger."$1\n"; $trigger = $2; } else { $trigger .= $ligne."\n"; } } } # on ecrit ce qui reste dans le script dans le fichier (normalement, il ne devrait rien rester a part des commentaires car toute commande est censee se terminer par un ;) print FICHIER_DESTINATION $trigger; # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_DESTINATION); print " OK\n"; } =pod =head3 traite_donnee Traite le fichier de donnees afin de modifier les rendre assimilables par PostgreSQL lors du COPY =cut sub traite_donnee { my ($parametre) = @_; my $dest=""; # nom du fichier de destination # modifie le nom du fichier source pour avoir le nom du fichier destination if($parametre =~ m/(.*\/)([^\/]+)$/){ $dest = "$1postgres_$2"; } else{ $dest = "postgres_$parametre"; } print FICHIERS_TRAITES "# Ecriture du fichier ".$parametre." vers le fichier :\n"; print FICHIERS_TRAITES "donnee = ".$dest."\n\n"; print "Traitement des donnees du fichier $parametre vers le fichier $dest : "; open(FICHIER_SOURCE, "<$parametre") or die ("le fichier $parametre ne peut pas etre ouvert\n"); open(FICHIER_DESTINATION, ">$dest") or die ("impossible d'ecrire dans le fichier destination : $dest\n"); if ($freebcp==vrai()){ # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # suppression des millisecondes et PM et modification des heures pour les horaires apres-midi $ligne =~ s/(\s+)(\d{1,2})(:\d{2}:\d{2}):\d{3}/$1.(($2%12)+12).$3/eg; # on ecrit la ligne dans le fichier de destination print FICHIER_DESTINATION $ligne; } } else { # on parcours toutes les lignes du fichier while (defined(my $ligne = )){ # suppression des espaces innutiles et remplacement par \null des vides en milieur de ligne #$ligne =~ s/\|\s*\|/\|\\null\|/g; #$ligne =~ s/\|\s*\|/\|\\null\|/g; # remplacement d'un vide par \null en fin de ligne #$ligne =~ s/\|s*$/|\\null/; # suppression des millisecondes et AM pour les horaires matin $ligne =~ s/:\d{3}AM//g; # suppression des millisecondes et PM et modification des heures pour les horaires apres-midi $ligne =~ s/(\s+)(\d{1,2})(:\d{2}:\d{2}):\d{3}PM/$1.(($2%12)+12).$3/eg; # on ecrit la ligne dans le fichier de destination print FICHIER_DESTINATION $ligne; } } # on ferme les fichiers ouverts close(FICHIER_SOURCE); close(FICHIER_DESTINATION); print " OK\n"; } 1; __END__ =head1 AUTEUR Virginie Quesnay =head1 BUGS Tous les fichiers bcp sont créés dans le même répertoire que le script sybase2postgresql =head1 VOIR AUSSI Documentation PostgreSQL, documentation BCP, ...