Export de scripts
Mon but était de scripter le DDL d'un serveur SQL entier (tables, vues, procédures, sécurité…) pour le stocker dans une arborescence de répertoires sur le disque. Cela me permet de le conserver dans un outil de gestion de version (CVS, subversion, au pire Sourcesafe). Afin d'assurer une certaine compatibilité avec un groupe d'outils très puissants pour déployer des structures de bases à partir de projets Sourcesafe, et développé en Perl : AbaPerls, j'ai adopté une certaine convention de nommage des objets, séparant par exemple pour les tables la création de la table elle-même de ses contraintes d'intégrité référentielle et de ses indexes. Cela permet de générer une base sans se préoccuper de l'ordre de création des objets, puisque les tables peuvent être toutes générées d'abord, et toutes les contraintes DRI appliquées ensuite.
Pour utiliser un fichier de configuration, je suis vite tombé sur yaml4r, une implémentation de YAML pour Ruby. Je ne connaissais pas YAML, dont le nom est encore un acronyme récursif (YAML Ain't a Markup Language) alors qu'il joue sur la construction habituelle du Yet Another… qui pourrait donner Yet Another Markup Language. C'est une initiative pour définir un langage très simple de description de contenu multiusage, à la fois descriptif et peu bavard (par opposition au XML). Il permet de structurer tout type d'information, aussi bien des tableaux, des listes, des logs qu'une sérialisation d'objets, sous une forme claire et très simple à comprendre. Ainsi on obtient par exemple un fichier de configuration hiérarchique aisé à maintenir et à lire, et parsable sans effort en Ruby. yaml4r fait maintenant partie des bibliothèques officielles livrées avec la distribution de Ruby. Vous n'aurez par besoin de les installer pour les utiliser.
Code Ruby
sqltools/extract.rb
require 'win32ole' module DMO end class Extract # == params == @@IncludeCollation = false def initialize() @s = WIN32OLE.new('SQLDMO.SQLServer') WIN32OLE.const_load(@s, DMO) # == globals params == @opToFile = DMO::SQLDMOScript_ToFileOnly # 64 @opScript = DMO::SQLDMOScript_PrimaryObject | DMO::SQLDMOScript_ObjectPermissions | DMO::SQLDMOScript_OwnerQualify @opTbl = DMO::SQLDMOScript_Indexes | @opScript @op2 = DMO::SQLDMOScript2_AnsiFile if not @IncludeCollation @op2 = @op2 | DMO::SQLDMOScript2_NoCollation end end #def __del__(self): # @s.DisConnect() def connect(server, integrated = true, login = nil, pwd = nil) @s.LoginSecure = integrated; @s.Connect(server, login, pwd); end def disconnect @s.DisConnect() end # script all server objects def scriptServer(curdir='.') $stdout.print "scripting server " + @s.Name + "\n\n" serverdir = curdir + '\\' + @s.Name + '\\' Dir.mkdir(serverdir) for db in @s.Databases if (not db.SystemObject) && (db.Status == DMO::SQLDMODBStat_Normal) $stdout.print "scripting database " + db.Name + "\n" dbdir = serverdir+'\\'+db.Name Dir.mkdir(dbdir) # -- tables -- Dir.mkdir(dbdir+'\\tbl') for tbl in db.Tables if not tbl.SystemObject script = tbl.Script(DMO::SQLDMOScript_ToFileOnly | @opTbl, dbdir+'\\tbl\\'+ tbl.Name() +'.tbl.sql', nil, @op2) # -- foreign keys -- fkeys = '' for fk in tbl.Keys if fk.Type == DMO::SQLDMOKey_Foreign fkeys += fk.Script(@opTbl, nil, @op2) end end if fkeys.length > 0 fkeys = fkeys.sub(/\r/,'') h = open(dbdir+'\\tbl\\'+ tbl.Name() +".fkey.sql", 'w') h.write(fkeys) h.close() end # -- indexes -- indexes = '' for idx in tbl.Indexes indexes += idx.Script(@opTbl, nil, @op2) end if indexes.length > 0 indexes = indexes.sub(/\r/, '') h = open(dbdir+'\\tbl\\'+ tbl.Name() +".ix.sql", 'w') h.write(indexes) h.close() end # -- triggers -- triggers = '' for tri in tbl.Triggers triggers += tri.Script(@opTbl, nil, @op2) end if triggers.length > 0 triggers = triggers.sub(/\r/, '') h = open(dbdir+'\\tbl\\'+ tbl.Name() +".tri.sql", 'w') h.write(triggers) h.close() end end end # -- views -- Dir.mkdir(dbdir+'\\view') for vw in db.Views if not vw.SystemObject script = vw.Script(@opToFile | @opTbl, dbdir+'\\view\\'+ vw.Name() +".view.sql") end end # -- sp -- Dir.mkdir(dbdir+'\\sp') for sp in db.StoredProcedures if not sp.SystemObject script = sp.Script(@opToFile | @opScript, dbdir+'\\sp\\'+ sp.Name() +".sp.sql") end end # -- udf -- Dir.mkdir(dbdir+'\\functions') for udf in db.UserDefinedFunctions if not udf.SystemObject script = udf.Script(@opToFile | @opScript, dbdir+'\\functions\\'+ udf.Name() +".sqlfun.sql") end end # -- security -- Dir.mkdir(dbdir+'\\security') for user in db.Users if not user.SystemObject username = user.Name() username = username.sub(/\\/, '_') script = user.Script(@opToFile | @opScript, dbdir+'\\security\\'+ username +".user.sql") end end for role in db.DatabaseRoles if not role.IsFixedRole script = role.Script(@opToFile | @opScript, dbdir+'\\security\\'+ role.Name() +".role.sql") end end # -- datatypes -- Dir.mkdir(dbdir+'\\datatypes') for type in db.UserDefinedDatatypes script = type.Script(@opToFile | @opScript, dbdir+'\\datatypes\\'+ type.Name() +".datatype.sql") end end end end end # generate INSERT statements for a table #def generateInserts(s, curdir='.', tableName, includeIdentity = false, top = 0):
Code d'appel de la classe Ruby
scriptDatabase.rb
# # I'm using a config file in YAML format : http://www.yaml.org/ # require "sqltools/extract" require 'yaml' conf = YAML::load( File.open( 'config.yaml' ) ) # create folder curdir = '.\\' + Time.now().strftime("%Y-%m-%d.%H%M") Dir.mkdir(curdir) e = Extract.new() for s in conf if conf[s[0]]['integrated'] == 1 e.connect(conf[s[0]]['host']) else e.connect(conf[s[0]]['host'], false, conf[s[0]]['login'], conf[s[0]]['pwd']) end e.scriptServer(curdir) e.disconnect() end
Exemple de fichier de configuration :
config.yaml
server1: host: 127.0.0.1 integrated: 0 login: mylogin pwd: mypassword server2: host: localhost integrated: 1
Le code est téléchargeable :



