A fokozatos fejlesztésnél az adatbázissémát nem a projekt elején próbáljuk megtervezni, hanem menet közben építjük fel, hogy tükrözze a megrendelők által támasztott követelmények változásait.
Akár tetszik, akár nem, a követelmények egy projekt előrehaladása közben mindig változnak.
Ez nem azt jelenti, hogy nem tervezünk!
A fontosabb kérdéseket előzetesen végig kell gondolnunk, tehát szükség lesz némi előzetes modellezésre, csupán a részleteket nem kell kidolgoznunk.
Ha automatizálni akarjuk az adatbázis építést, akkor az alábbi szempontok lehetnek fontosak:
A fenti igények és elvek figyelembevételével az alábbi megvalósítás lehet egy megoldás a problémára:
+ database_configuration/
+ database/
+ dump/
+ log/
+ schema/
+ scripts/
Itt találhatóak az adatbázisokról készített dump-ok.
Névük: <adatbázisnév>.dump
Minden adatbázisról kettő található. Az utólsó frissitéskor készült és az az előtti (<adatbázisnév>.old.dump)
Itt találhatóak az frissítések alkalmával keletkezett log-ok.
Nevük: <mysql date>.log (pl.: 2011-01-02.log)
Minden egyszerre, egy napon, frissített adatbázis logja egy fájlba kerül.
A logokat a script/log.sh script írja ki a képeernyőre és a fájlba.
A log egy sora:
2011-09-18 11:08:36+02:00 [OK] [<username>] Start script.
Itt találhatóak az adatbázis sémák. Minden séma egy külön könyvtárban.
A könyvtár neve a séma neve is egyben.
A könyvtárakban találhatóak a sémákat leíró fájlok.
Ezek a fájlok az alábbi típusuak lehetnek:
*.sql, *.php, *.py, *.sh
Itt találhatóak a végrehajtó scriptek.
A logolást segítő függvények találhatóak benne.
#!/bin/sh
#
LOG_PATH=`readlink -f "$SCRIPT_PATH/../database/log"`
LOG_FILENAME=$LOG_PATH/`date +"%Y-%m-%d"`.log
LEVEL_ERR="\033[1;31m"
LEVEL_WARN="\033[1;33m"
LEVEL_INFO="\033[36m"
LEVEL_OK="\033[0;0m"
LEVEL_SUCCESS="\033[1;32m"
LEVEL_LOG="\033[0;0m"
LAST_LOG_MESSAGE=""
log (){
echo -n "[ ] $@"
LAST_LOG_MESSAGE="$@"
}
dump (){
log $1
loge $2
}
log_to_file (){
echo "`date +"%F %T%:z"` [$1] [`whoami`] $2" >> $LOG_FILENAME
}
loge (){
if [ $1 -eq 0 ];then
echo "\r[${LEVEL_OK} OK ${LEVEL_LOG}]"
log_to_file "OK" "$LAST_LOG_MESSAGE"
elif [ $1 -eq 100 ];then
echo "\r[${LEVEL_ERR}FAIL${LEVEL_LOG}]"
log_to_file "ERR" "$LAST_LOG_MESSAGE"
elif [ $1 -eq 200 ];then
echo "\r[${LEVEL_SUCCESS} OK ${LEVEL_LOG}]"
log_to_file "OK" "$LAST_LOG_MESSAGE"
elif [ $1 -eq 250 ];then
echo "\r[${LEVEL_INFO}INFO${LEVEL_LOG}]"
log_to_file "INFO" "$LAST_LOG_MESSAGE"
elif [ $1 -eq 255 ];then
echo "\r[${LEVEL_WARN}WARN${LEVEL_LOG}]"
log_to_file "WARN" "$LAST_LOG_MESSAGE"
else
echo "\r[${LEVEL_ERR}ERR ${LEVEL_LOG}]"
log_to_file "ERR" "$LAST_LOG_MESSAGE"
exit $1
fi
}
A mysql.sh helpje. mysql.sh --help parancs futtatására kerül kiírásra.
USE mysql.sh [option0 value0] [option1 value1] ... [optionN valueN] FOR EXAMPLE mysql.sh -D test_db -u username -p password -uv 456 -s mySchemaName OPTIONS -s Schema name to be used. [REQUIRED] -h Connect to the MySQL server on the given host. [default=localhost] -u The MySQL user name to use when connecting to the server.[REQUIRED] -p The password to use when connecting to the server.[REQUIRED] -D The MySQL database to use. [REQUIRED] -P The TCP/IP port number to use for the connection. [default=3306] -uv The update version. [REQUIRED] --help
Ez az a szkript ami a schema-k alkalmazását végzi.
Az alapállapot az, hogy van telepítve a gépen egy mysql szerver. Nem kell több! Hiszen pont az a lényeg, hogy egyszerűen, egy szkript futtatásával felépítsük az adatbázisunkat.
Ahhoz, hogy meg tudjuk állapítai egy adatbázisról, hogy milyen verziójú, azt nyilván kell tartani. Legkézenfekvőbb, ha ezt magában az adatbázisban tesszük. Ez annyiból fog állni, hogy létrehozunk egy táblát, aminek lesz egy mezője és egy rekokrdja, ami az adatbázis séma verzióját fogja tárolni. Tehát egy teljesen üres adatbázis csak ezt az egy táblát fogja tartalmazni, és mivel ez egy üres adatvázis a verziója nulla lesz. Tehát minden sémának lesz egy 0. verziója. A 0.sql:
CREATE TABLE `database_configuration` ( `version` INT NOT NULL , PRIMARY KEY ( `version` ) ) ENGINE = MYISAM ; INSERT INTO `database_configuration` ( `version` ) VALUES ( '0' );
Így teljes egészében a skripre bízhatjuk az adatbázis felépítését. Az adatbázis létrehozásától az aktualis sémák alkalmazásáig.
A skriptet bárhonnan elindíthatjuk.
mysql.sh -D test_db -u username -p password -uv 5 -s test1
A szkript elején a változók definiálása és a log.sh includolása történik.
#!/bin/sh # DB_HOST="localhost" DB_USER="" DB_PWD="" DB_NAME="" DB_PORT="3306" DB_DEFAULT_CHARACTER_SET="utf8 COLLATE utf8_general_ci" MYSQL_CMD="mysql" SCRIPT_FILE=`readlink -f $0` SCRIPT_PATH=`dirname $SCRIPT_FILE` SCHEMA_PATH=`readlink -f "$SCRIPT_PATH/../database/schema"` SCHEMA_SUFFIX="sql" SCHEMA_SCRIPT_SUFFIX="sh php py" SCHEMA_NAME="" DUMP_PATH=`readlink -f "$SCRIPT_PATH/../database/dump"` DUMP_FILE="$DUMP_PATH/dbdump.dump" current_schema_version=0 update_schema_version=0 . "$SCRIPT_PATH/log.sh"
Ha a --help kapcsolóval indítjuk, akkor kiírja a read.me fájl tartalmát és kilép.
mysql.sh --help
if [ "$1" = "--help" ]
then
cat "$SCRIPT_PATH/read.me"
exit 0
fi
Ha nem, akkor a megadott paraméterek alapján beállítja a változókat.
dump "Start script." 0
for i in $*
do
case $i in
-*) p=$i ;;
*)
case $p in
"-s") SCHEMA_NAME=$i;;
"-h") DB_HOST=$i;;
"-u") DB_USER=$i;;
"-p") DB_PWD=$i;;
"-D") DB_NAME=$i;;
"-P") DB_PORT=$i;;
"-uv") update_schema_version=$i;;
*) dump "Invalid option: $p" 255;;
esac
p="";;
esac
done
Ha nem kerül megadásra adatbázis és séma név, akkor hibával kilép. Ha a szkript futása közben bérmilyen hiba keletkezik, jelzi azt és kilép.
if [ -z "$DB_NAME" ]; then
dump "Missing database name." 1
fi
if [ -z "$SCHEMA_NAME" ]; then
dump "Missing schema name." 1
fi
Majd ezek után beállítja a MYSQL_CMD értékét.
MYSQL_CMD="mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PWD"
Majd ellenőrzi az adatbázist
check_database $DB_NAME
A mysql a information_schema.schemata táblában tárolja az adatbézisok egyes adatait, mint pl. a nevűket. Tehát, ha itt szerepel a létrehozandó adatbázisunk neve, akkor már létezik, ha nem, akkor nem létezik, és létre kell hozni.
check_database (){
log "Check database ($1)."
sql="SELECT schema_name FROM schemata WHERE schema_name='$1'"
db=`$MYSQL_CMD -D information_schema -N -e "$sql"`
if [ -z "$db" ]; then
loge 255
create_database $DB_NAME
use_schema 0
else
loge 0
fi
}
Ha nem létezik, akkor létrehozzuk az adatbázist, és alkalmazzuka a 0. sémát.
create_database (){
log "Create database ($1)."
sql="CREATE DATABASE $1 DEFAULT CHARACTER SET $DB_DEFAULT_CHARACTER_SET;"
$MYSQL_CMD -e "$sql" > /dev/null 2>&1
loge $?
}
Majd lekérdezzük az adatbázisunk verzióját.
get_schema_version
És értékül adjuk az current_schema_version változónak.
get_schema_version (){
log "Get current schema version."
sql="SELECT * FROM database_configuration;"
current_schema_version=`$MYSQL_CMD -D $DB_NAME -N -e "$sql"` > /dev/null 2>&1
loge $?
}
Ezek után ellenőrizzük, hogy az adatbázisunk verziója kissebb-e mint az kivánt verzió. S ha igen, akkor elkezdődik az update folyamat.
Elsőször is készítünk az adatbázisról egy dump-ot. Így bármi történik a frissítés alatt, vissza tudjuk állítani a korábbi állpotot.
make_dump
make_dump (){
log "Make database dump($DB_NAME.dump)."
dump_filename=$DUMP_PATH/$DB_NAME.dump
if [ -f $dump_filename ]; then
cp -fr $dump_filename "$DUMP_PATH/$DB_NAME.old.dump"
fi
mysqldump -h $DB_HOST -u $DB_USER -p$DB_PWD -B $DB_NAME > $dump_filename
loge $?
}
Ha kész, akkor egyenkét alkalmazzuk a sémékt.
update_schema (){
for i in `seq $1 $2`;do
use_schema $i
set_schema_version $i
done
}
Minden séma fájl egyenkét kerül alakalmazásra.
use_schema (){
log "The $1. schema updating."
filename="$SCHEMA_PATH/$SCHEMA_NAME/$1"
cmd="$MYSQL_CMD -D $DB_NAME"
tmp=`mktemp`
res=1
if [ -f "$filename.$SCHEMA_SUFFIX" ]; then
res=0
$cmd < "$filename.$SCHEMA_SUFFIX" > $tmp 2>&1
if [ $? -ne 0 ]; then
loge 100
err=`cat $tmp`
rm -f $tmp
dump "$err" 1
fi
else
for suffix in $SCHEMA_SCRIPT_SUFFIX;do
if [ -f "$filename.$suffix" ]; then
res=0
loge 250
log "'$1.$suffix' schema script executing."
chmod +x "$filename.$suffix"
"$filename.$suffix" $DB_NAME $DB_HOST $DB_PORT $DB_USER $DB_PWD > $tmp 2>&1
if [ $? -ne 0 ]; then
loge 100
err=`cat $tmp`
rm -f $tmp
dump "$err" 1
fi
fi
done
fi
rm -f $tmp
loge $res
}
Alapesetben minden séma egy SQL utasítás és egy *.sql nevű fájlban található. Ilyenkor csak egyszerüen alkalmazásra kerül a séma.
Előfordulhatnak olyan helyzetek is, amikor egy egyszerű SQL utasítás nem elég, mert nem lehet vele végrehajtani a változtatásokat. Ilyenkor scriptet kell írni.
A szkript három féle típust támogat (sh php py). Ha a séma ezek egyike, akkor azt futtatja, és az alábbi változók értékét ebben a sorrrendben kapja meg: $DB_NAME $DB_HOST $DB_PORT $DB_USER $DB_PWD. Ha a szkript nem tér vissza hibával, akkor tovább alkalmazza a sémákat.
Ha egy séma sikeresen alkalmazásra került, akkor beállítja az adatbázis új verzióját:
set_schema_version (){
log "Schema version updating ($i)."
sql="UPDATE database_configuration SET version = '$1';"
$MYSQL_CMD -D $DB_NAME -e "$sql" 1>/dev/null
loge $?
}
Ha minden séma alkalmazása sikeresen befejeződött, akkor Schema uptating successful. üzenettel kilép.
Ez a megvalósítás letölthető: database_configuration.tar.gz
Felhasznált irodalom: