nutools/sqlmig

1698 lines
57 KiB
Plaintext
Raw Permalink Normal View History

#!/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
Les mises à jours sont dans un répertoire UPDATEDIR, qui contient un répertoire
pour chaque base de données à gérer. Ces répertoires sont des DATABASEDIR. Les
fichiers sql peuvent être placés dans le répertoire UPDATEDIR pour les mises à
jour administratives, ou dans DATABASEDIR pour les mises à jour de la base de
données.
Les fichiers sql peuvent contenir dans les premières lignes du fichier un tag
@sqlmig sous forme de commentaire. On peut avoir:
-- @sqlmig drop
identifie une suite de requêtes à exécuter pour supprimer la base de données
et recommencer à zéro
-- @sqlmig create
identifie une suite de requêtes à exécuter pour créer la base de données.
-- @sqlmig admin
identifie une mise à jour administrative. Les fichiers situés dans UPADTEDIR
sont automatiquement considérés comme des mises à jour administratives même
en l'absence de ce tag.
Les mises à jour administratives sont exécutées avec les paramètres du fichier
my.cnf (resp. ora.conf) et sont typiquement utilisées pour créer les comptes
utilisateurs pour la connexion à la base de données et leur attribuer des droits
d'accès. Ces mises à jour sont exécutées sans sélectionner une base de données
en particulier.
Les mises à jour utilisateurs sont exécutées avec les paramètres du fichier
my.cnf (resp. ora.conf) augmenté du fichier my-DBNAME.cnf (res. ora-DBNAME.conf)
Elles sont exécutées avec la base de données concernée sélectionnée.
Il y a deux modes d'exécution: prod ou devel. En mode prod, la suppression de
base de données est interdite. De plus, tous les fichiers de mise à jour de la
forme *.devel.sql sont ignorés. Cela permet de fignoler une mise à jour sans
risquer de l'exécuter en production.
Les fichiers de la forme maint-*.sql sont ignorés sauf s'ils sont explicitement
sélectionnés avec l'option -f ce qui permet de suivre les opérations de
maintenance faites sur la 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
--suffix SUFFIX
Lors de la connexion à la base de données, toujours ajouter le suffixe
spécifié au nom de la base de données. Cette valeur peut être spécifiée
dans la section [sqlmig] du fichier my.cnf ou avec la variable SUFFIX
pour ora.conf
--csv2sql
Convertir les fichiers CSV en SQL. C'est la valeur par défaut. Ce
paramètre peut être spécifié dans my.cnf sous la forme
[sqlmig]
csv2sql=1
ou dans ora.conf
CSV2SQL=1
Pour le moment, cette conversion n'est supportée que pour MySQL. Tous
les fichiers de la forme [NUM]TABLE[-data].csv sont transformés en une
suite d'insertion dans la table TABLE. Une variante insère les données
dans la table après l'avoir vidée avec 'truncate table'. Les fichiers
doivent être de la forme [NUM]TABLE-data_truncate.csv
--csv-null VALUE
Lors de la conversion des fichiers .csv en .sql, considérer que VALUE
représente la valeur NULL. Par défaut, utiliser la chaine vide
--csv-null-empty
--csv-null-mysql
--csv-null-upper
Aliases pour --csv-null '', --csv-null '\\N' et --csv-null NULL respectivement
--no-csv2sql
Ne pas convertir les fichiers CSV en SQL. Ce paramètre peut être
spécifié dans my.cnf sous la forme
[sqlmig]
csv2sql=0
ou dans ora.conf
CSV2SQL=0
--load-data DEFAULT|LOCAL|SERVER
Si l'option --no-csv2sql est activée, spécifier le type de chargement
effectué pour les fichiers CSV.
* Avec LOCAL (la valeur par défaut), la directive 'load data local' est
utilisée. Cependant les erreurs éventuelles sont ignorées parce que le
serveur n'a pas de moyen pour arrêter la transmission des informations
par le client.
* Avec SERVER, la directive 'load data' est utilisée. Les erreurs
éventuelles sont affichées et arrêtent l'importation des données. Par
contre, le fichier doit être accessible par le serveur MySQL puisque
les données sont chargées directement par le serveur.
NB: avec MariaDB sur systemd, l'accès à /home, /root et /run/user est
désactivé par défaut. Pour pouvoir utiliser ce mode avec des fichiers
de l'utilisateur, il faut lancer ces commandes:
[ \$(id -u) == 0 ] && sudo= || sudo=sudo
\$sudo mkdir -p /etc/systemd/system/mariadb.service.d
\$sudo tee /etc/systemd/system/mariadb.service.d/dontprotecthome.conf <<<\$'[Service]\nProtectHome=false'
\$sudo systemctl daemon-reload
* Avec DEFAULT, utiliser SERVER si on communique avec le serveur MySQL
local (c'est à dire si host n'est pas spécifié ou vaut 127.*, ::1 ou
localhost). Sinon, utiliser LOCAL
2018-05-04 10:13:21 +04:00
--profile PROFILE
-P, --prod
-T, --test
Activer le profil spécifié. Les options -P et -T sont des aliases pour
les options --profile prod et --profile test respectivement.
Quand un profil est activé, les fichiers de configuration par défaut
deviennent respectivement \$PROFILE-my.cnf et \$PROFILE-ora.conf au lieu
de my.cnf et ora.conf. De plus, ces fichiers sont aussi recherchés dans
2018-05-06 11:50:45 +04:00
le répertoire /etc/sqlmig, en fonction du mode: en mode production, on
cherche d'abord dans /etc/sqlmig. En mode développement, on cherche
d'abord dans le répertoire local.
2018-05-04 10:13:21 +04:00
--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
2018-05-30 07:18:48 +04:00
-c, --connect
Se connecter avec le client natif (mysql ou sqlplus) sur la base de
données courante.
-r, --restore-test
Restaurer une sauvegarde d'une base de production sur la test. Cette
option n'est (actuellement) supportée que pour MySQL.
L'argument attendu est un fichier de sauvegarde DATABASE.sql[.gz]
effectué avec la commande
mysqldump --databases --add-drop-database DATABASE
Dans le fichier spécifié, on remplace toutes les occurences de DATABASE
par DATABASE_test, ensuite on lance (re)création de la base de données
mysql <DATABASE_test.sql
2018-05-22 10:23:05 +04:00
--update-all
Mettre à jour la base de données. C'est l'option par défaut
-t, --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.
-b, --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.
Notez que le suffixe spécifié avec l'option --suffix est toujours
rajouté au nom de la base de données.
2018-06-04 15:37:14 +04:00
-f, --update-one UPDATES...
Forcer l'application des mises à jour spécifiées. Ne pas mettre à jour
2018-06-12 10:44:01 +04:00
l'état des mises à jour installées.
Important: Avec cette option, tout se passe comme si les seuls fichiers
existant sont ceux spécifiés. Par exemple, quel que soit l'ordre dans
lequel les fichiers sont spécifiés, ils sont évalués dans l'ordre
alphanumérique et sont ignorés s'ils n'ont pas l'extension .sql
-n, --fake
Ne pas faire les mises à jour, afficher simplement ce qui serait fait
--force, --continue-on-error
Ne pas s'arrêter en cas d'erreur de mise à jour
--no-data-csv
Ne pas procéder à la conversion des fichiers CSV en SQL
--force-data-csv
Forcer la conversion des fichiers CSV en SQL. Par défaut, la conversion
n'est faite que si le fichier csv est plus récent que le fichier sql
correspondant.
--devel-mode
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 mconf_get() {
local cnf="$1" section="$2" name="$3"
awkrun <"$cnf" section="$section" name="$name" '
BEGIN { in_section = 0 }
!in_section && $0 == "[" section "]" { in_section = 1; next }
in_section && $0 == "[" section "]" { in_section = 0; next }
in_section && $0 ~ "^" name " *=" {
gsub(/^[^=]*= */, "")
print
exit
}
'
}
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 "$@"
}
function ensure_dbtype() {
local dir="$1" type="$2"
if [ "$type" == auto ]; then
if [ -f "$dir/my.cnf" ]; then
dbtype=mysql
elif [ -f "$dir/ora.conf" ]; then
dbtype=oracle
else
die "Vous devez spécifier le type --mysql ou --oracle"
fi
else
dbtype="$type"
fi
}
function ensure_dbmode() {
local dbtype="$1" mode="$2"
if [ "$mode" == auto ]; then
2018-05-06 11:50:45 +04:00
local profile_mode
if [ -n "$profile" ]; then
profile_mode="${profile}_PROFILE_MODE"
profile_mode="${!profile_mode}"
fi
if [ -n "$profile_mode" ]; then
dbmode="$profile_mode"
elif [ "$dbtype" == mysql ]; then
is_root && dbmode=prod || dbmode=devel
else
dbmode=prod
fi
else
dbmode="$mode"
fi
}
2018-05-30 07:18:48 +04:00
function set_dbdirs() {
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
}
function fix_csv2sql() {
if [ -z "$csv2sql" ]; then
# valeur par défaut
csv2sql=1
elif is_yes "$csv2sql"; then
csv2sql=1
elif is_no "$csv2sql"; then
csv2sql=0
fi
}
function fix_csv_null() {
# si csv_null a une valeur vide, c'est déjà la valeur par défaut
case "$csv_null" in
empty) csv_null=;;
mysql) csv_null='\N';;
upper) csv_null=NULL;;
esac
}
function fix_load_data() {
if [ -z "$load_data" ]; then
# valeur par défaut
load_data=local
else
case "${load_data,,}" in
default|d*) load_data=default;;
local|l*) load_data=local;;
server|s*) load_data=server;;
esac
fi
if [ "$load_data" == default ]; then
case "$host" in
""|localhost|127.*|::1) load_data=server;;
*) load_data=local;;
esac
fi
}
function get_csvinfo() {
# afficher les informations sur un fichier csv: nom de la table, et s'il
# faut faire un truncate
local csvname="$(basename -- "$1")"
local script='{
truncate = ($0 ~ /-data_truncate(.devel)?.csv$/)? "1": ""
sub(/^.*\//, "")
sub(/^(([A-Z][-.A-Z0-9]*[0-9]-?)|([0-9][-.0-9]*-?))/, "")
sub(/\.csv$/, "")
sub(/\.devel$/, "")
sub(/-data(_[a-z]+*)?$/, "")
print "truncate=" truncate
gsub(/'\''/, "'\'\\\\\'\''")
print "table='\''" $0 "'\''"
}'
awk "$script" <<<"$csvname"
}
################################################################################
# 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() {
[ -n "$fake" ] && return
# s'assurer que la table des mises à jour existe
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
}
2018-05-04 10:13:21 +04:00
function mysql_get_defaults() {
local dir="$1"
2018-05-06 11:50:45 +04:00
if [ -n "$profile" ]; then
if [ "$dbmode" == devel ]; then
if [ -f "$dir/${profile}-my.cnf" ]; then
echo "$dir/${profile}-my.cnf"; return
elif [ -f "$PROFILEDIR/${profile}-my.cnf" ]; then
echo "$PROFILEDIR/${profile}-my.cnf"; return
fi
else
if [ -f "$PROFILEDIR/${profile}-my.cnf" ]; then
echo "$PROFILEDIR/${profile}-my.cnf"; return
elif [ -f "$dir/${profile}-my.cnf" ]; then
echo "$dir/${profile}-my.cnf"; return
fi
fi
2018-05-04 10:13:21 +04:00
fi
2018-05-06 11:50:45 +04:00
echo "$dir/my.cnf"
2018-05-04 10:13:21 +04:00
}
function mysql_set_adminargs() {
local dir="$1" defaults
adminargs=()
setx defaults=mysql_get_defaults "$dir"
2018-05-04 10:13:21 +04:00
if [ -f "$defaults" ]; then
array_add adminargs --defaults-file="$defaults"
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
}
function mysql__mconf_get() {
local defaults="$1" tmp
if [ -n "$set_suffix" ]; then
setx tmp=mconf_get "$defaults" sqlmig suffix
[ -n "$tmp" ] && suffix="$tmp"
fi
if [ -n "$set_csv2sql" ]; then
setx tmp=mconf_get "$defaults" sqlmig csv2sql
[ -n "$tmp" ] && {
csv2sql="$tmp"
fix_csv2sql
}
fi
if [ -n "$set_csv_null" ]; then
setx tmp=mconf_get "$defaults" sqlmig csv_null
[ -n "$tmp" ] && {
csv_null="$tmp"
fix_csv_null
}
fi
if [ -n "$set_load_data" ]; then
if [ -z "$host" ]; then
# load_data==default requière host
setx host=mconf_get "$defaults" client host
fi
setx tmp=mconf_get "$defaults" sqlmig load-data
[ -n "$tmp" ] && {
load_data="$tmp"
fix_load_data
}
fi
}
function mysql_set_userargs() {
local dir="$1" dbname="$2" defaults
local set_suffix set_csv2sql set_csv_null set_load_data
userargs=()
setx defaults=mysql_get_defaults "$dir"
[ -z "$suffix" ] && set_suffix=1
[ -z "$csv2sql" ] && set_csv2sql=1
[ -z "$csv_null" ] && set_csv_null=1
[ -z "$load_data" ] && set_load_data=1
2018-05-04 10:13:21 +04:00
if [ -f "$defaults" ]; then
array_add userargs --defaults-file="$defaults"
mysql__mconf_get "$defaults"
if [ -n "$userdefaults" ]; then
array_add userargs --defaults-extra-file="$userdefaults"
mysql__mconf_get "$userdefaults"
elif [ -f "$dir/my-${dbname}.cnf" ]; then
array_add userargs --defaults-extra-file="$dir/my-${dbname}.cnf"
mysql__mconf_get "$dir/my-${dbname}.cnf"
fi
elif [ -n "$userdefaults" ]; then
array_add userargs --defaults-file="$userdefaults"
mysql__mconf_get "$userdefaults"
elif [ -f "$dir/my-${dbname}.cnf" ]; then
array_add userargs --defaults-file="$dir/my-${dbname}.cnf"
mysql__mconf_get "$dir/my-${dbname}.cnf"
fi
[ ${#userargs[*]} -gt 0 ] || array_add userargs --default-character-set utf8
# initialiser les valeurs par défaut
fix_csv2sql
fix_csv_null
fix_load_data
}
function mysql_set_mysqlargs() {
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
}
MYSQL_ADMIN_CONF_DONE=
function mysql_admin_update() {
local name="$1" update="$2" updateone="$3" done
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
if [ -z "$MYSQL_ADMIN_CONF_DONE" ]; then
MYSQL_ADMIN_CONF_DONE=1
mysql_tbconf
fi
if ! setx done=mysql_get_done "$name"; then
[ -n "$fake" ] || die
fi
[ -n "$done" ] && return
fi
estep "$name"
[ -n "$fake" ] && return
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
mysql_before_update || die
fi
cat "$update" | mysql_admin_ve || abort_on_error
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
mysql_after_update || die
fi
}
function mysql_user_update() {
local name="$1" update="$2" dbname="$3" updateone="$4" done
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
if ! setx done=mysql_get_done "$name" "$dbname"; then
[ -n "$fake" ] || die
fi
[ -n "$done" ] && return
fi
estep "$name"
[ -n "$fake" ] && return
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
mysql_before_update "$dbname" || die
fi
if [[ "$update" == *.sql ]]; then
# SQL
cat "$update" | mysql_user_ve || abort_on_error
else
# CSV
local truncate table local sql
eval "$(get_csvinfo "$update")"
[ -n "$truncate" ] && sql="truncate table $table;"
[ "$load_data" == local ] && local=1 || local=
sql="${sql}load data${local:+ local} infile '$update' into table \`$table\`
character set 'utf8'
fields terminated by ','
optionally enclosed by '\\\"'
escaped by '\\\\'
lines terminated by '\\n'
starting by ''
ignore 1 lines
($(<"$update" awk '{ print; exit }'));"
echo "$sql" | mysql_user_ve || abort_on_error
fi
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
mysql_after_update "$dbname" || die
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() {
[ -n "$fake" ] && return
# 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
}
function oracle_ensure_opdir() {
if [ -z "$OPDIR" ]; then
ac_set_tmpdir OPDIR
>"$SQLMIGLOG"
fi
}
2018-05-04 10:13:21 +04:00
function oracle_get_defaults() {
local dir="$1"
2018-05-06 11:50:45 +04:00
if [ -n "$profile" ]; then
if [ "$dbmode" == devel ]; then
if [ -f "$dir/${profile}-ora.conf" ]; then
echo "$dir/${profile}-ora.conf"; return
elif [ -f "$PROFILEDIR/${profile}-ora.conf" ]; then
echo "$PROFILEDIR/${profile}-ora.conf"; return
fi
else
if [ -f "$PROFILEDIR/${profile}-ora.conf" ]; then
echo "$PROFILEDIR/${profile}-ora.conf"; return
elif [ -f "$dir/${profile}-ora.conf" ]; then
echo "$dir/${profile}-ora.conf"; return
fi
fi
2018-05-04 10:13:21 +04:00
fi
2018-05-06 11:50:45 +04:00
echo "$dir/ora.conf"
2018-05-04 10:13:21 +04:00
}
function oracle_source_adminconf() {
local dir="$1"
unset ORACLE_SID NLS_LANG ADMINCONNECT USERCONNECT SQLMIGLOG
setx defaults=oracle_get_defaults "$dir"
2018-05-04 10:13:21 +04:00
[ -f "$defaults" ] && source "$defaults"
[ -n "$admindefaults" ] && {
source "$admindefaults" || die
}
[ -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
[ -n "$SQLMIGLOG" ] || SQLMIGLOG="/tmp/sqlmig-${ORACLE_SID}-${dbname}.log"
}
function oracle_source_userconf() {
local dir="$1" dbname="$2"
unset ORACLE_SID NLS_LANG ADMINCONNECT USERCONNECT SQLMIGLOG SUFFIX CSV2SQL CSV_NULL
setx defaults=oracle_get_defaults "$dir"
2018-05-04 10:13:21 +04:00
[ -f "$defaults" ] && source "$defaults"
[ -f "$dir/ora-${dbname}.conf" ] && source "$dir/ora-${dbname}.conf"
[ -n "$userdefaults" ] && {
source "$userdefaults" || die
}
[ -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"
[ -z "$suffix" ] && suffix="$SUFFIX"
[ -z "$csv2sql" ] && csv2sql="$CSV2SQL"
[ -z "$csv_null" ] && csv_null="$CSV_NULL"
fix_csv2sql
fix_csv_null
}
ORACLE_ADMIN_CONF_DONE=
function oracle_admin_update() {
local name="$1" update="$2" updateone="$3" done
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
if [ -z "$ORACLE_ADMIN_CONF_DONE" ]; then
ORACLE_ADMIN_CONF_DONE=
oracle_tbconf
fi
setx done=oracle_get_done "$name" || die
[ -n "$done" ] && return
fi
estep "$name"
[ -n "$fake" ] && return
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
oracle_before_update || die
fi
cat "$update" | oracle_admin_ve || abort_on_error
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
oracle_after_update || die
fi
}
function oracle_user_update() {
local name="$1" update="$2" dbname="$3" updateone="$4" done
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
setx done=oracle_get_done "$name" "$dbname" || die
[ -n "$done" ] && return
fi
estep "$name"
[ -n "$fake" ] && return
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
oracle_before_update "$dbname" || die
fi
cat "$update" | oracle_user_ve || abort_on_error
if [ -z "$updateone" ]; then
2018-05-15 10:02:43 +04:00
oracle_after_update "$dbname" || die
fi
}
################################################################################
2018-05-04 10:13:21 +04:00
MODE=auto
PROFILEDIR=/etc/sqlmig
2018-05-06 11:50:45 +04:00
prod_PROFILE_MODE=prod
test_PROFILE_MODE=devel
2018-05-04 10:13:21 +04:00
set_defaults sqlmig
admindefaults=
userdefaults=
user=
password=
pwset=
host=
port=
socket=
charset=
oracle_sid=
nls_lang=
suffix=
csv2sql=
csv_null=
load_data=
2018-05-04 10:13:21 +04:00
profile=
type=auto
action=update
updatedir=
exportdir=
dbdir=
dbname=
updateone=
force=
data_csv=auto
2018-05-04 10:13:21 +04:00
mode="$MODE"
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=
--suffix: suffix=
--csv2sql csv2sql=1
--csv-null: csv_null=
--csv-null-empty csv_null=empty
--csv-null-mysql csv_null='\N'
--csv-null-upper csv_null=NULL
--no-csv2sql csv2sql=0
--load-data: load_data=
2018-05-04 10:13:21 +04:00
--profile: profile=
-P,--prod profile=prod
-T,--test profile=test
--mysql type=mysql
--oracle type=oracle
-0,--init action=init
-e:,--export: '$action=export; set@ exportdir'
2018-05-30 07:18:48 +04:00
-c,--connect action=connect
-r,--restore-test action=restore_test
2018-05-22 10:23:05 +04:00
--update-all action=update
-t:,--updatedir: updatedir=
-d:,--databasedir: dbdir=
-b:,--database: dbname=
-f,--update-one updateone=1
-n,--fake fake=1
--force,--continue-on-error force=1
--no-data-csv data_csv=
--force-data-csv data_csv=force
--prod-mode-dangerous mode=prod
--devel-mode 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 config/sqlmig; then
enote "Autosélection config/sqlmig/"
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
2018-12-08 15:48:12 +04:00
if [ ! -f "$dbdir/02grants.sql" ]; then
estep "02grants.sql"
echo >"$dbdir/02grants.sql" "\
-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
-- @sqlmig admin
create user '$dbname' identified by '$dbname';
grant all privileges on $dbname.* to '$dbname';"
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
[sqlmig]
#suffix="
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=
[sqlmig]
#csv2sql=0
csv_null=
#load-data=default"
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
2018-12-08 15:48:12 +04:00
create user $dbname identified by \"$dbname\";
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=FRENCH_FRANCE.AL32UTF8
#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\"
# divers
#SUFFIX=
#CSV2SQL=0
CSV_NULL="
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" "*my.cnf" "my-*.cnf" "*ora.conf" "ora-*.conf"
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"
for update in "${updates[@]}"; do
[ -d "$destdir" ] || mkdir -p "$destdir"
[[ "$update" == "*.devel.sql" ]] && continue
copy_update "$update" "$destdir"
done
eend; eclearp
done
exit 0
2018-05-30 07:18:48 +04:00
################################################################################
elif [ "$action" == connect ]; then
set_dbdirs
force_dbname="$dbname"
force_suffix="$suffix"
for dbdir in "${dbdirs[@]}"; do
dbname="$force_dbname"
[ -n "$dbname" ] || setx dbname=basename "$dbdir"
suffix="$force_suffix"
etitle "$dbname"
ensure_dbtype "$dbdir" "$type"
ensure_dbmode "$dbtype" "$mode"
if [ "$dbtype" == mysql ]; then
# construire les paramètres pour mysql
mysql_set_userargs "$dbdir" "$dbname"
mysql_set_mysqlargs
if [ -n "$suffix" ]; then
estepi "Suffixe: $dbname --> $dbname$suffix"
dbname="$dbname$suffix"
fi
array_del mysqlargs -B # désactiver le mode batch
mysql "${userargs[@]}" "${mysqlargs[@]}" -D "$dbname"
elif [ "$dbtype" == oracle ]; then
# lire les paramètres
oracle_source_userconf "$dbdir" "$dbname"
oracle_ensure_opdir
if [ -n "$suffix" ]; then
estepi "Suffixe: $dbname --> $dbname$suffix"
dbname="$dbname$suffix"
fi
sqlplus "$USERCONNECT@$ORACLE_SID" ${USERDBA:+as sysdba} "$@"
else
die "BUG: $dbtype: type non implémenté"
fi
eend
done
exit 0
################################################################################
elif [ "$action" == restore_test ]; then
set_dbdirs
if [ ${#dbdirs[*]} -gt 1 ]; then
die "Avec --restore-test, une seule base de données doit être spécifiée"
fi
dbdir="${dbdirs[0]}"
[ -n "$dbname" ] || setx dbname=basename "$dbdir"
etitle "$dbname"
ensure_dbtype "$dbdir" "$type"
ensure_dbmode "$dbtype" "$mode"
[ "$dbtype" == mysql ] || die "Seule les bases de type MySQL sont supportées"
# construire les paramètres pour mysql
mysql_set_userargs "$dbdir" "$dbname"
mysql_set_mysqlargs
if [ -n "$suffix" ]; then
estepi "Suffixe: $dbname --> $dbname$suffix"
dbname="$dbname$suffix"
fi
dump="$1"
[ -n "$dump" ] || die "Vous devez spécifier le fichier de dump"
[ -f "$dump" ] || die "$dump: fichier de dump introuvable"
ac_set_tmpdir tmpdir
if [[ "$dump" == *.gz ]]; then
gzip -dc "$dump" >"$tmpdir/prod.sql"
elif [[ "$dump" == *.sql ]]; then
cat "$dump" >"$tmpdir/prod.sql"
else
die "$dump: n'est pas un fichier sql"
fi
dump="$tmpdir/prod.sql"
pname="$dbname"
tname="${dbname}_test"
sed <"$tmpdir/prod.sql" >"$tmpdir/test.sql" "\
s/\`$pname\`/\`$tname\`/g
s/\`${pname}_updates_\`/\`${tname}_updates_\`/g"
enote "Vous allez restaurer un fichier de sauvegarde de le base $pname vers la base $tname"
ask_yesno "Voulez-vous continuer?" X || die
array_del mysqlargs -B # désactiver le mode batch
mysql "${userargs[@]}" "${mysqlargs[@]}" <"$tmpdir/test.sql"
ac_clean "$tmpdir"
exit 0
################################################################################
elif [ "$action" != update ]; then
die "BUG: $action: action non implémentée"
fi
################################################################################
# update
2018-05-30 07:18:48 +04:00
set_dbdirs
if [ -n "$updateone" ]; then
updatefiles=()
for updatefile in "$@"; do
array_add updatefiles "$(abspath "$updatefile")"
done
fi
function should_update() {
local update="$1" name
setx name=basename "$update"
if [ -n "$updateone" ]; then
# prendre tous les fichiers sélectionnés par -f
array_contains updatefiles "$update"
return $?
elif [ "${name#maint-}" != "$name" ]; then
# ignorer les opérations de maintenance par défaut
return 1
elif [ "$dbmode" != devel -a "${name%.devel.*}" != "$name" ]; then
# si on est en mode autre que devel, le nom ne doit pas se terminer
# par .devel.*
return 1
fi
return 0
}
2018-05-15 10:02:43 +04:00
# répertoire temporaire pour Oracle
OPDIR=
if [ -n "$updatedir" ]; then
## mises à jour administratives
array_lsfiles updates "$updatedir" "*.sql"
if [ ${#updates[*]} -gt 0 ]; then
ensure_dbtype "$updatedir" "$type"
ensure_dbmode "$dbtype" "$mode"
if [ "$dbtype" == mysql ]; then
# construire les paramètres pour mysql
mysql_set_adminargs "$updatedir"
mysql_set_mysqlargs
# Mises à jour
etitled "Mises à jour admin"
for update in "${updates[@]}"; do
should_update "$update" || continue
mysql_admin_update "${update#$updatedir/}" "$update" "$updateone"
done
eend; eclearp
elif [ "$dbtype" == oracle ]; then
# lire les paramètres
oracle_source_adminconf "$updatedir"
oracle_ensure_opdir
# Mises à jour
etitled "Mises à jour"
for update in "${updates[@]}"; do
should_update "$update" || continue
oracle_admin_update "${update#$updatedir/}" "$update" "$updateone"
done
eend; eclearp
fi
fi
fi
## mises à jour utilisateur
force_dbname="$dbname"
force_suffix="$suffix"
for dbdir in "${dbdirs[@]}"; do
dbname="$force_dbname"
[ -n "$dbname" ] || setx dbname=basename "$dbdir"
suffix="$force_suffix"
etitle "$dbname"
ensure_dbtype "$dbdir" "$type"
ensure_dbmode "$dbtype" "$mode"
############################################################################
if [ "$dbtype" == mysql ]; then
# construire les paramètres pour mysql
mysql_set_adminargs "$dbdir"
mysql_set_userargs "$dbdir" "$dbname"
mysql_set_mysqlargs
if is_yes "$csv2sql" && [ -n "$data_csv" ]; then
# Conversion csv --> sql
array_lsfiles csvs "$dbdir" "*.csv"
etitled "Conversion"
for csv in "${csvs[@]}"; do
setx csvname=basename -- "$csv"
sql="${csv%.csv}.sql"
if [ "$data_csv" != force ]; then
testnewer "$csv" "$sql" || continue
fi
estep "$csvname --> ${csvname%.csv}.sql"
eval "$(get_csvinfo "$csvname")"
mysqlloadcsv_args=(
${truncate:+-T}
-Z "$csv_null"
--prefix "-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8"
-nIf "$csv" "$table"
)
"$scriptdir/mysqlloadcsv" "${mysqlloadcsv_args[@]}" >"$sql"
done
eend; eclearp
fi
# lister les mises à jour disponibles
drops=()
creates=()
updates=()
have_csv=
array_lsfiles files "$dbdir" "*.sql" "*.csv"
for file in "${files[@]}"; do
if [[ "$file" == *.sql ]]; then
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
elif [ ! -f "${file%.csv}.sql" ]; then
# n'ajouter le CSV que si le fichier SQL correspondant n'existe pas
array_add updates "$file"
have_csv=1
fi
done
if [ -n "$suffix" ]; then
estepi "Suffixe: $dbname --> $dbname$suffix"
dbname="$dbname$suffix"
fi
if is_no "$csv2sql" && [ -n "$have_csv" ]; then
estepi "Chargement des fichiers CSV avec la méthode $load_data"
fi
# 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
should_update "$update" || continue
setx name=basename "$drop"
estep "$name"
[ -n "$fake" ] && continue
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
should_update "$update" || continue
setx name=basename "$create"
estep "$name"
[ -n "$fake" ] && continue
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"
for update in "${updates[@]}"; do
should_update "$update" || continue
if [[ "$update" == *.sql ]]; then
# fichier SQL
if have_tag admin "$update"; then
[ -n "$updatedir" ] && name="${update#$updatedir/}" || name="${update#$dbdir/}"
mysql_admin_update "$name" "$update" "$updateone"
else
mysql_user_update "${update#$dbdir/}" "$update" "$dbname" "$updateone"
fi
elif is_no "$csv2sql"; then
# fichier CSV, ne les traiter que si on est en mode --no-csv2sql
mysql_user_update "${update#$dbdir/}" "$update" "$dbname" "$updateone"
fi
done
eend; eclearp
############################################################################
elif [ "$dbtype" == oracle ]; then
[ -n "$OPDIR" ] || ac_set_tmpdir OPDIR
# lire les paramètres
oracle_source_userconf "$dbdir" "$dbname"
oracle_ensure_opdir
# lister les mises à jour disponibles
drops=()
creates=()
updates=()
have_csv=
array_lsfiles files "$dbdir" "*.sql" "*.csv"
for file in "${files[@]}"; do
if [[ "$file" == *.sql ]]; then
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
elif [ ! -f "${file%.csv}.sql" ]; then
# n'ajouter le CSV que si le fichier SQL correspondant n'existe pas
array_add updates "$file"
have_csv=1
fi
done
if [ -n "$suffix" ]; then
estepi "Suffixe: $dbname --> $dbname$suffix"
dbname="$dbname$suffix"
fi
# 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
should_update "$update" || continue
setx name=basename "$drop"
estep "$name"
[ -n "$fake" ] && continue
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
should_update "$update" || continue
setx name=basename "$create"
estep "$name"
[ -n "$fake" ] && continue
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"
for update in "${updates[@]}"; do
should_update "$update" || continue
if [[ "$update" == *.sql ]]; then
# fichier SQL
if have_tag admin "$update"; then
[ -n "$updatedir" ] && name="${update#$updatedir/}" || name="${update#$dbdir/}"
oracle_admin_update "$name" "$update" "$updateone"
else
oracle_user_update "${update#$dbdir/}" "$update" "$dbname" "$updateone"
fi
fi
done
eend; eclearp
############################################################################
else
die "BUG: $dbtype: type non implémenté"
fi
eend
done