From b5e2417514cca467ef6223c5cd19254b29867f2e Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 2 May 2018 22:18:47 +0400 Subject: [PATCH] =?UTF-8?q?cr=C3=A9ation=20de=20sqlmig=20pour=20g=C3=A9rer?= =?UTF-8?q?=20les=20mises=20=C3=A0=20jour=20sur=20une=20base=20de=20donn?= =?UTF-8?q?=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmig | 964 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 964 insertions(+) create mode 100755 sqlmig diff --git a/sqlmig b/sqlmig new file mode 100755 index 0000000..adaaff1 --- /dev/null +++ b/sqlmig @@ -0,0 +1,964 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +source "$(dirname -- "$0")/lib/ulib/ulib" || exit 1 +urequire DEFAULTS install + +function display_help() { + uecho "$scriptname: gérer les mises à jour sur une base de données + +USAGE + $scriptname [options] + +OPTIONS + -g, --admin-defaults-file ADMINDEFAULTS + Spécifier un fichier de configuration à utiliser pour se connecter à la + base de données pour les opérations de maintenance comme la création ou + la suppression de la base de données. + * Dans le mode MySQL, ce fichier est chargé en plus du fichier par + défaut my.cnf + * Dans le mode Oracle, ce fichier est chargé en plus du fichier par + défaut ora.conf. + -C, --defaults-file USERDEFAULTS + Spécifier un fichier de configuration à utiliser pour se connecter à la + base de données pour les opérations de mise à jour. + * Dans le mode MySQL, ce fichier est chargé en plus du fichier par + défaut my-DATABASE.cnf + * Dans le mode Oracle, ce fichier est chargé en plus du fichier par + défaut ora-DATABASE.conf. + -u, --user USER + -p, --password PASSWORD + -h, --host HOST + --port PORT + --socket SOCKET + --character-set CHARSET + -s, --oracle-sid ORACLE_SID + --nls-lang NLS_LANG + Spécifier les valeurs pour la connexion à la base de données. Les + options --host, --port et --socket ne sont valides que pour MySQL. Ces + valeurs remplacent les valeurs par défaut chargées depuis my.cnf + Les options --sid et --nls-lang ne sont valides que pour Oracle. Ces + valeurs remplacent les valeurs par défaut chargées depuis ora.conf + + --mysql + --oracle + Spécifier le type de base de données à gérer. Par défaut, on gère une + base de type mysql. + Avec le type oracle, il faut adapter la lecture de cette documentation: + à chaque fois que l'on parle de base de données, il s'agit en réalité de + gérer un utilisateur. La connexion à la base de données proprement dite + est configurée dans le fichier ora.conf + -0, --init + Créer les fichiers initiaux pour gérer une base de données. Cette option + est utilisée pour le développement + -e, --export DESTUPDATEDIR + Exporter les définitions de bases de données et mises à jour du + répertoire courant vers le répertoire DESTUPDATEDIR + -u, --update + Mettre à jour la base de données. C'est l'option par défaut + -b, --updatedir UPDATEDIR + Spécifier le répertoire qui contient les répertoires de mises à jour + pour chaque base de données. + -d, --databasedir DATABASEDIR + Spécifier le répertoire qui contient les mises à jour à appliquer pour + une base de données spécifique. Le nom de la base de données à gérer est + déterminé à partir du nom du répertoire. Si cette option n'est pas + spécifiée, tous les répertoires de base de données de UPDATEDIR sont + considérés. + -n, --database DATABASE + Spécifier le nom de la base de données. En principe le nom de la base de + données est calculé à partir du nom du répertoire DATABASEDIR. Cette + option peut être utilisée par exemple pour créer une base de test à + partir des définitions d'une base de prod. + + --force, --continue-on-error + Ne pas s'arrêter en cas d'erreur de mise à jour + --devel + Activer le mode développement. Ce mode est automatiquement activé si + l'utilisateur courant n'est pas root. La suppression des bases de + données n'est autorisée qu'en mode développement + -Z, --recreate + Supprimer la base de données puis la recréer et appliquer les mises à + jour. + --drop-only + Supprimer la base de données uniquement. Ne pas la recréer. + --create-only + Créer la base de données uniquement. Ne pas appliquer les mises à jour" +} + +function __check_devel_dir() { + # $1 = P (le préfixe) + # parentdir et cwd doivent être définis. initialiser le cas échéant la + # variable updatedir + local updatedir dbdir + if [ "${parentdir%/$1}" != "$parentdir" ]; then + # On est dans un répertoire de la forme P/$1/DB, autosélectioner P/$1 + upvar updatedir "$parentdir" + upvar dbdir "$cwd" + return 0 + elif [ "${cwd%/$1}" != "$cwd" ]; then + # On est dans un répertoire de la forme P/$1, autosélectioner P/$1 + upvar updatedir "$cwd" + return 0 + elif [ -d "$cwd/$1" ]; then + # On est dans un répertoire P tel que P/$1 existe, autosélectioner P/$1 + upvar updatedir "$cwd/$1" + return 0 + fi + return 1 +} +function __check_mysql_prod_dir() { + # parentdir et cwd doivent être définis + local dir updatedir dbdir + if [ "${parentdir%/updates}" != "$parentdir" ]; then + # Si on est dans un répertoire de la forme P/updates/DB, alors + # sélectioner P/updates si P contient le marqueur .mysqld-update + dir="$parentdir" + dbdir="$cwd" + elif [ -d "$cwd/updates" ]; then + # Si on est dans un répertoire P tel que P/updates existe, alors + # sélectioner P/updates si P contient le marqueur .mysqld-update + dir="$cwd/updates" + fi + if [ -n "$dir" -a -f "$(dirname -- "$dir")/.mysqld-update" ]; then + upvar updatedir "$dir" + [ -n "$dbdir" ] && upvar dbdir "$dbdir" + return 0 + fi + return 1 +} + +function find_mysqldupdatedir() { + # en commençant à partir du répertoire $1 qui vaut par défaut le répertoire + # courant, chercher un répertoire contenant le fichier témoin .mysqld-update + local dir="$1" origdir + [ -n "$dir" ] || dir="$(pwd)" + setx dir=abspath "$dir" + origdir="$dir" + + while true; do + if [ -f "$dir/.mysqld-update" ]; then + echo "$dir" + return 0 + fi + if [ -z "$dir" -o "$dir" == / -o "$dir" == "$HOME" ]; then + echo "$origdir" + return 1 + fi + setx dir=dirname -- "$dir" + done +} +function check_mysqldupdatedir() { + local exportdir="$1" + [ -f "$exportdir/.mysqld-update" ] || die "$(ppath "$exportdir"): n'est pas un répertoire mysqld-update" +} + +function have_tag() { + # tester si le fichier $2 a le tag "@sqlmig $1" + <"$2" awk '{print; if ($0 == "") exit}' | quietgrep '^-- *@sqlmig *'"$1"' *$' +} + +function abort_on_error() { + [ -z "$force" ] && die "$@" +} + +################################################################################ +# MySQL + +function get_mysql_admindb() { echo "sqlmig_admin_db_"; } +function get_mysql_admintb() { echo "sqlmig_updates_"; } +function get_mysql_usertb() { echo "${1}_updates_"; } + +function mysql_ve() { + #local r; set -x #DEBUG + mysql "${@:3}" ${2:+-D "$2"} ${1:+-e "$1"} + #r=$?; set +x; return $r #DEBUG +} +function mysql_user_ve() { + mysql_ve "$1" "$dbname" "${userargs[@]}" "${mysqlargs[@]}" "${@:2}" +} +function mysql_user_qe() { + if show_debug; then mysql_user_ve "$@" + elif show_verbose; then mysql_user_ve "$@" >/dev/null + else mysql_user_ve "$@" >&/dev/null + fi +} +function mysql_admin_ve() { + mysql_ve "$1" "$2" "${adminargs[@]}" "${mysqlargs[@]}" "${@:3}" +} +function mysql_admin_qe() { + if show_debug; then mysql_admin_ve "$@" + elif show_verbose; then mysql_admin_ve "$@" >/dev/null + else mysql_admin_ve "$@" >&/dev/null + fi +} +function mysql_tbconf() { + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_mysql_admindb + if ! mysql_admin_qe "select 1" "$dbname"; then + mysql_admin_qe "create database $dbname" + fi + [ -n "$tb" ] || setx tb=get_mysql_admintb "$dbname" + if ! mysql_admin_qe "select count(*) from $tb" "$dbname"; then + mysql_admin_qe "create table $tb ( + name varchar(128) not null primary key +,tem_done int(1) +,date_start datetime +,date_done datetime +)" "$dbname" || die "create table $tb" + fi + else + # user + [ -n "$tb" ] || setx tb=get_mysql_usertb "$dbname" + if ! mysql_user_qe "select count(*) from $tb"; then + mysql_user_qe "create table $tb ( + name varchar(128) not null primary key +,tem_done int(1) +,date_start datetime +,date_done datetime +)" || die "create table $tb" + fi + fi +} +function mysql_get_done() { + local name="$1" dbname="$2" tb="$3" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_mysql_admindb + [ -n "$tb" ] || setx tb=get_mysql_admintb "$dbname" + mysql_admin_ve "select name from $tb where name = '$name' and tem_done = 1" "$dbname" -N + else + # user + [ -n "$tb" ] || setx tb=get_mysql_usertb "$dbname" + mysql_user_ve "select name from $tb where name = '$name' and tem_done = 1" -N + fi +} +function mysql_before_update() { + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_mysql_admindb + [ -n "$tb" ] || setx tb=get_mysql_admintb "$dbname" + mysql_admin_ve "insert into $tb (name, tem_done, date_start) values ('$name', 0, sysdate()) on duplicate key update tem_done = 0, date_start = sysdate(), date_done = null" "$dbname" + else + # user + [ -n "$tb" ] || setx tb=get_mysql_usertb "$dbname" + mysql_user_ve "insert into $tb (name, tem_done, date_start) values ('$name', 0, sysdate()) on duplicate key update tem_done = 0, date_start = sysdate(), date_done = null" + fi +} +function mysql_after_update() { + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_mysql_admindb + [ -n "$tb" ] || setx tb=get_mysql_admintb "$dbname" + mysql_admin_ve "update $tb set tem_done = 1, date_done = sysdate() where name = '$name'" "$dbname" + else + # user + [ -n "$tb" ] || setx tb=get_mysql_usertb "$dbname" + mysql_user_ve "update $tb set tem_done = 1, date_done = sysdate() where name = '$name'" + fi +} + +################################################################################ +# Oracle + +function get_oracle_admindb() { echo "sqlmig_admin_db_"; } +function get_oracle_admintb() { echo "sqlmig_updates_"; } +function get_oracle_usertb() { echo "${1}_updates_"; } + +function oracle_sqlplus() { + # lancer sqlplus sans affichage superflu, et spooler vers $SQLMIGLOG + #local r; set -x #DEBUG + echo "\ +set pagesize 0 +set feedback off +set linesize 8192 +set tab off +whenever oserror exit failure +whenever sqlerror exit sql.sqlcode +spool '$SQLMIGLOG' append" >"$OPDIR/login.sql" + local connect="$1@$ORACLE_SID" sysdba="$2"; shift; shift + ORACLE_PATH="$OPDIR" sqlplus -S "$connect" ${sysdba:+as sysdba} "$@" + #r=$?; set +x; return $r #DEBUG +} +function qe() { + # lancer la commande $@: si elle retourne un code d'erreur, afficher le + # résultat de la commande sur stderr si on est en mode verbeux, sinon ne + # rien afficher + local r output + output="$("$@")"; r=$? + if [ ${#output} -gt 0 ]; then + show_verbose && echo "$output" 1>&2 + fi + return $r +} + +function oracle_admin_query() { + # lancer sqlplus avec la connexion admin + oracle_sqlplus "$ADMINCONNECT" "$ADMINDBA" "$@" +} +function oracle_admin_ve() { + # lancer une requête admin, ne pas masquer le résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + oracle_admin_query <<<"$*" + else + oracle_admin_query + fi +} +function oracle_admin_qe() { + # lancer une requête admin en masquant le résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + qe oracle_admin_query <<<"$*" + else + qe oracle_admin_query + fi +} +function oracle_admin_ne() { + # lancer une requête admin et retourner vrai si elle affiche un résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + [ -n "$(oracle_admin_query <<<"$*")" ] + else + [ -n "$(oracle_admin_query)" ] + fi +} +function oracle_admin_have_user() { + # tester en mode admin si le user $1 existe + oracle_admin_ne "select username from all_users where username = '${1^^}';" +} +function oracle_admin_have_table() { + # tester en mode admin si le table $1 existe (avec éventuellement le owner $2) + local sql="select table_name from all_tables where table_name = '${1^^}'" + [ -n "$2" ] && sql="$sql and owner = '${2^^}'" + oracle_admin_ne "$sql;" +} + +function oracle_user_query() { + # lancer sqlplus avec la connexion user + oracle_sqlplus "$USERCONNECT" "$USERDBA" "$@" +} +function oracle_user_ve() { + # lancer une requête user, ne pas masquer le résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + oracle_user_query <<<"$*" + else + oracle_user_query + fi +} +function oracle_user_qe() { + # lancer une requête user en masquant le résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + qe oracle_user_query <<<"$*" + else + qe oracle_user_query + fi +} +function oracle_user_ne() { + # lancer une requête user et retourner vrai si elle affiche un résultat + if [ $# -gt 0 ]; then + show_debug && edebug "query: $*" + [ -n "$(oracle_user_query <<<"$*")" ] + else + [ -n "$(oracle_user_query)" ] + fi +} +function oracle_user_have_user() { + # tester en mode user si le user $1 existe + oracle_user_ne "select username from all_users where username = '${1^^}';" +} +function oracle_user_have_table() { + # tester en mode user si le table $1 existe (avec éventuellement le owner $2) + local sql="select table_name from all_tables where table_name = '${1^^}'" + [ -n "$2" ] && sql="$sql and owner = '${2^^}'" + oracle_user_ne "$sql;" +} + +function oracle_tbconf() { + # s'assurer que la table des mises à jour existe + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_oracle_admindb + #if ! oracle_admin_have_user "$dbname"; then + # oracle_admin_ve "create user $dbname; grant connect to $dbname;" + #fi + [ -n "$tb" ] || setx tb=get_oracle_admintb "$dbname" + local owner="${ADMINCONNECT%%/*}" + [ -n "owner" ] || owner=system + if ! oracle_admin_have_table "$tb" "$owner"; then + oracle_admin_ve "create table $tb ( + name varchar(128) not null primary key +,tem_done number(1) +,date_start timestamp +,date_done timestamp +);" || die "create table $tb" + fi + else + # user + [ -n "$tb" ] || setx tb=get_oracle_usertb "$dbname" + local owner="${USERCONNECT%%/*}" + [ -n "owner" ] || owner=system + if ! oracle_user_have_table "$tb" "$owner"; then + oracle_user_ve "create table $tb ( + name varchar(128) not null primary key +,tem_done number(1) +,date_start timestamp +,date_done timestamp +);" || die "create table $tb" + fi + fi +} +function oracle_get_done() { + # afficher le nom d'une mise à jour si elle a été appliquée + local name="$1" dbname="$2" tb="$3" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_oracle_admindb + [ -n "$tb" ] || setx tb=get_oracle_admintb "$dbname" + oracle_admin_ve "select name from $tb where name = '$name' and tem_done = 1;" + else + # user + [ -n "$tb" ] || setx tb=get_oracle_usertb "$dbname" + oracle_user_ve "select name from $tb where name = '$name' and tem_done = 1;" + fi +} +function oracle_before_update() { + # préparer l'exécution d'une mise à jour + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_oracle_admindb + [ -n "$tb" ] || setx tb=get_oracle_admintb "$dbname" + oracle_admin_ve "\ +merge into $tb d +using (select '$name' name from dual) s +on (d.name = s.name) +when matched then update set d.tem_done = 0, d.date_start = sysdate, d.date_done = null +when not matched then insert (name, tem_done, date_start) values ('$name', 0, sysdate); +commit;" + else + # user + [ -n "$tb" ] || setx tb=get_oracle_usertb "$dbname" + oracle_user_ve "\ +merge into $tb d +using (select '$name' name from dual) s +on (d.name = s.name) +when matched then update set d.tem_done = 0, d.date_start = sysdate, d.date_done = null +when not matched then insert (name, tem_done, date_start) values ('$name', 0, sysdate); +commit;" + fi +} +function oracle_after_update() { + # valider l'exécution d'une mise à jour + local dbname="$1" tb="$2" + if [ -z "$dbname" ]; then + # admin + setx dbname=get_oracle_admindb + [ -n "$tb" ] || setx tb=get_oracle_admintb "$dbname" + oracle_admin_ve "\ +update $tb set tem_done = 1, date_done = sysdate where name = '$name'; +commit;" + else + # user + [ -n "$tb" ] || setx tb=get_oracle_usertb "$dbname" + oracle_user_ve "\ +update $tb set tem_done = 1, date_done = sysdate where name = '$name'; +commit;" + fi +} + +################################################################################ + +admindefaults= +userdefaults= +user= +password= +pwset= +host= +port= +socket= +charset= +oracle_sid= +nls_lang= +type=auto +action=update +updatedir= +exportdir= +dbdir= +dbname= +force= +mode=auto +drop= +drop_only= +create_only= +args=( + --help '$exit_with display_help' + -g:,--admin-defaults-file: admindefaults= + -C:,--defaults-file: userdefaults= + -u:,--user: user= + -p:,--password: '$set@ password; pwset=1' + -h:,--host: host= + --port: port= + --socket: socket= + --character-set: charset= + -s:,--oracle-sid: oracle_sid= + --nls-lang: nls_lang= + --mysql type=mysql + --oracle type=oracle + -0,--init action=init + -e:,--export: '$action=export; set@ exportdir' + -u,--update action=update + -b:,--updatedir: updatedir= + -d:,--databasedir: dbdir= + -n:,--database: dbname= + --force,--continue-on-error force=1 + --prod-dangerous mode=prod + --devel mode=devel + -Z,--recreate drop=1 + --drop-only '$drop=1; drop_only=1' + --create-only create_only=1 +) +parse_args "$@"; set -- "${args[@]}" + +setx cwd=pwd +if [ -z "$dbdir" -a -z "$updatedir" ]; then + setx parentdir=dirname -- "$cwd" + if __check_devel_dir src/main/resources/database; then + enote "Autosélection src/main/resources/database/" + elif __check_devel_dir support/database; then + enote "Autosélection support/database/" + elif __check_devel_dir database; then + enote "Autosélection database/" + elif __check_mysql_prod_dir; then + [ "$type" == auto ] && type=mysql + fi +fi + +[ -n "$updatedir" ] && setx updatedir=abspath "$updatedir" +[ -n "$dbdir" ] && setx dbdir=abspath "$dbdir" + +################################################################################ +if [ "$action" == init ]; then + [ -n "$dbdir" -a -z "$dbname" ] && setx dbname=basename "$dbdir" + [ -n "$dbname" ] || dbname="$1" + read_value ${dbname:+-i} "Entrez le nom de la base de données" dbname "$dbname" + + if [ -z "$dbdir" -a -n "$updatedir" ]; then + dbdir="$updatedir/$dbname" + elif [ -z "$dbdir" ]; then + dbdir="$dbname" + fi + read_value ${dbdir:+-i} "Entrez le répertoire dans lequel créer les définitions" dbdir "$dbdir" + setx dbdir=abspath "$dbdir" + + ask_yesno "Voulez-vous créer les fichiers initiaux pour la base de données $dbname dans le répertoire $(ppath "$dbdir")?" O || die + + estep "Création du répertoire $dbdir" + [ -d "$dbdir" ] || mkdir -p "$dbdir" || die + + [ "$type" == auto ] && type=mysql + + if [ "$type" == mysql ]; then + if [ ! -f "$dbdir/00dropdb.sql" ]; then + estep "00dropdb.sql" + echo >"$dbdir/00dropdb.sql" "\ +-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +-- @sqlmig drop + +drop database if exists @@database@@;" + fi + + if [ ! -f "$dbdir/01createdb.sql" ]; then + estep "01createdb.sql" + echo >"$dbdir/01createdb.sql" "\ +-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +-- @sqlmig create + +create database @@database@@;" + fi + + if [ ! -f "$dbdir/my.cnf" ]; then + estep "my.cnf" + echo >"$dbdir/my.cnf" "\ +# Paramètres de connexion par défaut +[client] +#user= +#password= +#host=localhost +#port=3306 +#socket=/var/run/mysqld/mysqld.sock + +[mysql] +default-character-set=utf8" + fi + + if [ ! -f "$dbdir/my-${dbname}.cnf" ]; then + estep "my-${dbname}.cnf" + echo >"$dbdir/my-${dbname}.cnf" "\ +# Paramètres de connexion pour $dbname +[client] +#user= +#password=" + fi + + elif [ "$type" == oracle ]; then + if [ ! -f "$dbdir/00dropuser.sql" ]; then + estep "00dropuser.sql" + echo >"$dbdir/00dropuser.sql" "\ +-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +-- @sqlmig drop + +drop user $dbname cascade;" + fi + + if [ ! -f "$dbdir/01createuser.sql" ]; then + estep "01createuser.sql" + echo >"$dbdir/01createuser.sql" "\ +-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +-- @sqlmig create + +create user $dbname identified by \"password\"; +grant connect to $dbname; +grant resource to $dbname;" + fi + + if [ ! -f "$dbdir/ora.conf" ]; then + estep "ora.conf" + echo >"$dbdir/ora.conf" "\ +# Paramètres de connexion par défaut +ORACLE_SID=orcl +NLS_LANG=AMERICAN_AMERICA.AL32UTF8 +# paramètres de connexion pour les mises à jour administratives +# si aucune valeur n'est spécifiée, la valeur effective est '/ as sysdba' mais +# cela requière que la mise à jour soit faite avec une connexion locale. +ADMINCONNECT= +# paramètres de connexion pour les mises à jour utilisateur +USERCONNECT=$dbname/password +# logs des mises à jour +SQLMIGLOG=\"/tmp/sqlmig-\${ORACLE_SID}-${dbname}.log\"" + fi + + else + die "BUG: $type: type non implémenté" + fi + + exit 0 + +################################################################################ +elif [ "$action" == export ]; then + [ -n "$exportdir" ] || setx exportdir=find_mysqldupdatedir + check_mysqldupdatedir "$exportdir" + setx exportdir=abspath "$exportdir/updates" + + if [ -n "$dbdir" ]; then + dbdirs=("$dbdir") + elif [ -z "$updatedir" ]; then + array_lsfiles files . "*.sql" + if [ ${#files[*]} -gt 0 ]; then + enote "Autosélection répertoire courant" + dbdir="$cwd" + dbdirs=("$dbdir") + else + die "Vous devez spécifier l'option -b" + fi + elif [ -n "$dbname" ]; then + dbdirs=("$updatedir/$dbname") + else + array_lsdirs dbdirs "$updatedir" + fi + + for dbdir in "${dbdirs[@]}"; do + setx dbname=basename -- "$dbdir" + # TEMPLATE.d est spécial dans mysqld-update: il faut l'ignorer + [ "$dbname" != TEMPLATE.d ] || continue + + etitled "$dbname" + destdir="$exportdir/$dbname" + array_lsfiles updates "$dbdir" "*.sql" + for update in "${updates[@]}"; do + [ -d "$destdir" ] || mkdir -p "$destdir" + copy_update "$update" "$destdir" + done + eend; eclearp + done + + exit 0 + +################################################################################ +elif [ "$action" != update ]; then + die "BUG: $action: action non implémentée" +fi + +################################################################################ +# update + +if [ -n "$dbdir" ]; then + dbdirs=("$dbdir") +elif [ -z "$updatedir" ]; then + array_lsfiles files . "*.sql" + if [ ${#files[*]} -gt 0 ]; then + enote "Autosélection répertoire courant" + dbdir="$cwd" + dbdirs=("$dbdir") + else + die "Vous devez spécifier l'option -b" + fi +elif [ -n "$dbname" ]; then + dbdirs=("$updatedir/$dbname") +else + array_lsdirs dbdirs "$updatedir" +fi + +if [ -n "$dbname" -a ${#dbdirs[*]} -gt 1 ]; then + die "Avec l'option -n, une seule base de données doit être spécifiée" +fi + +# répertoire temporaire pour Oracle +OPDIR= + +force_dbname="$dbname" +for dbdir in "${dbdirs[@]}"; do + dbname="$force_dbname" + [ -n "$dbname" ] || setx dbname=basename "$dbdir" + etitle "$dbname" + + if [ "$type" == auto ]; then + if [ -f "$dbdir/my.cnf" ]; then + dbtype=mysql + elif [ -f "$dbdir/ora.conf" ]; then + dbtype=oracle + else + die "Vous devez spécifier le type --mysql ou --oracle" + fi + else + dbtype="$type" + fi + if [ "$mode" == auto ]; then + if [ "$dbtype" == mysql ]; then + is_root && dbmode=prod || dbmode=devel + else + dbmode=prod + fi + else + dbmode="$mode" + fi + + # lister les mises à jour disponibles + drops=() + creates=() + updates=() + array_lsfiles files "$dbdir" "*.sql" + for file in "${files[@]}"; do + if have_tag drop "$file"; then + array_add drops "$file" + elif have_tag create "$file"; then + array_add creates "$file" + else + array_add updates "$file" + fi + done + + ############################################################################ + if [ "$dbtype" == mysql ]; then + # construire les paramètres pour mysql + adminargs=() + if [ -f "$dbdir/my.cnf" ]; then + array_add adminargs --defaults-file="$dbdir/my.cnf" + if [ -n "$admindefaults" ]; then + array_add adminargs --defaults-extra-file="$admindefaults" + fi + elif [ -n "$admindefaults" ]; then + array_add adminargs --defaults-file="$admindefaults" + fi + [ ${#adminargs[*]} -gt 0 ] || array_add adminargs --default-character-set utf8 + + userargs=() + if [ -f "$dbdir/my.cnf" ]; then + array_add userargs --defaults-file="$dbdir/my.cnf" + if [ -n "$userdefaults" ]; then + array_add userargs --defaults-extra-file="$userdefaults" + elif [ -f "$dbdir/my-${dbname}.cnf" ]; then + array_add userargs --defaults-extra-file="$dbdir/my-${dbname}.cnf" + fi + elif [ -n "$userdefaults" ]; then + array_add userargs --defaults-file="$userdefaults" + elif [ -f "$dbdir/my-${dbname}.cnf" ]; then + array_add userargs --defaults-file="$dbdir/my-${dbname}.cnf" + fi + [ ${#userargs[*]} -gt 0 ] || array_add userargs --default-character-set utf8 + + mysqlargs=() + [ -n "$user" ] && array_add mysqlargs -u "$user" + [ -n "$pwset" ] && array_add mysqlargs -p"$password" + [ -n "$host" ] && array_add mysqlargs -h "$host" + [ -n "$port" ] && array_add mysqlargs -P "$port" + [ -n "$socket" ] && array_add mysqlargs -S "$socket" + [ -n "$charset" ] && array_add mysqlargs --default-character-set "$charset" + [ -n "$force" ] && array_add mysqlargs -f + array_add mysqlargs -B + + # Suppression + if [ -n "$drop" ]; then + [ "$dbmode" == devel ] || die "La suppression de base de données n'est pas autorisée en mode production" + etitle "Suppression" + if ! ask_yesno "Etes-vous sûr de vouloir supprimer la base de données $dbname?" X; then + ewarn "Suppression annulée, les autres opérations ne seront pas effectuées" + eend; continue + fi + for drop in "${drops[@]}"; do + setx name=basename "$drop" + estep "$name" + sed "s/@@database@@/$dbname/g" "$drop" | mysql_admin_qe || abort_on_error "drop: $name" + done + eend + [ -n "$drop_only" ] && { eend; continue; } + fi + + # Création + if ! mysql_admin_qe "select 1" "$dbname"; then + etitled "Création" + for create in "${creates[@]}"; do + setx name=basename "$create" + estep "$name" + sed "s/@@database@@/$dbname/g" "$create" | mysql_admin_qe || abort_on_error "create: $name" + done + eend; eclearp + fi + [ -n "$create_only" ] && { eend; continue; } + + # Mises à jour + etitled "Mises à jour" + mysql_tbconf "$dbname" + admin_conf=1 + for update in "${updates[@]}"; do + setx name=basename "$update" + if have_tag admin "$update"; then + if [ -n "$admin_conf" ]; then + admin_conf= + mysql_tbconf + fi + setx done=mysql_get_done "$name" || die + [ -n "$done" ] && continue + + estep "$name" + mysql_before_update || die + cat "$update" | mysql_admin_ve || abort_on_error + mysql_after_update || die + else + setx done=mysql_get_done "$name" "$dbname" || die + [ -n "$done" ] && continue + + estep "$name" + mysql_before_update "$dbname" || die + cat "$update" | mysql_user_ve || abort_on_error + mysql_after_update "$dbname" || die + fi + done + eend; eclearp + + ############################################################################ + elif [ "$dbtype" == oracle ]; then + [ -n "$OPDIR" ] || ac_set_tmpdir OPDIR + + # lire les paramètres + unset ORACLE_SID NLS_LANG ADMINCONNECT USERCONNECT SQLMIGLOG + if [ -n "$admindefaults" ]; then + source "$admindefaults" || die + elif [ -f "$dbdir/ora.conf" ]; then + source "$dbdir/ora.conf" + fi + if [ -n "$userdefaults" ]; then + source "$userdefaults" || die + elif [ -f "$dbdir/ora-${dbname}.conf" ]; then + source "$dbdir/ora-${dbname}.conf" + fi + [ -n "$oracle_sid" ] && ORACLE_SID="$oracle_sid" + [ -n "$nls_lang" ] && NLS_LANG="$nls_lang" + export ORACLE_SID NLS_LANG + if [ -n "$ADMINCONNECT" ]; then + ADMINDBA= + else + ADMINCONNECT=/ + ADMINDBA=1 + fi + if [ -n "$USERCONNECT" ]; then + USERDBA= + else + USERCONNECT=/ + USERDBA=1 + fi + [ -n "$SQLMIGLOG" ] || SQLMIGLOG="/tmp/sqlmig-${ORACLE_SID}-${dbname}.log" + >"$SQLMIGLOG" + + # Suppression + if [ -n "$drop" ]; then + [ "$dbmode" == devel ] || die "La suppression de user n'est pas autorisée en mode production" + etitle "Suppression" + if ! ask_yesno "Etes-vous sûr de vouloir supprimer le user $dbname?" X; then + ewarn "Suppression annulée, les autres opérations ne seront pas effectuées" + eend; continue + fi + for drop in "${drops[@]}"; do + setx name=basename "$drop" + estep "$name" + sed "s/@@database@@/$dbname/g" "$drop" | oracle_admin_ve || abort_on_error "drop: $name" + done + eend + [ -n "$drop_only" ] && { eend; continue; } + fi + + # Création + if ! oracle_admin_have_user "$dbname"; then + etitled "Création" + for create in "${creates[@]}"; do + setx name=basename "$create" + estep "$name" + sed "s/@@database@@/$dbname/g" "$create" | oracle_admin_ve || abort_on_error "create: $name" + done + eend; eclearp + fi + [ -n "$create_only" ] && { eend; continue; } + + # Mises à jour + etitled "Mises à jour" + oracle_tbconf "$dbname" + admin_conf=1 + for update in "${updates[@]}"; do + setx name=basename "$update" + if have_tag admin "$update"; then + if [ -n "$admin_conf" ]; then + admin_conf= + oracle_tbconf + fi + setx done=oracle_get_done "$name" || die + [ -n "$done" ] && continue + + estep "$name" + oracle_before_update || die + cat "$update" | oracle_admin_ve || abort_on_error + oracle_after_update || die + else + setx done=oracle_get_done "$name" "$dbname" || die + [ -n "$done" ] && continue + + estep "$name" + oracle_before_update "$dbname" || die + cat "$update" | oracle_user_ve || abort_on_error + oracle_after_update "$dbname" || die + fi + done + eend; eclearp + + ############################################################################ + else + die "BUG: $dbtype: type non implémenté" + fi + + eend +done