database_loader 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in database_loader.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ Database Loader
2
+ ===============
3
+
4
+ Load SQL views, materialized views, grants, etc. into database.
5
+
6
+ For tables, use Rails migrations.
7
+
8
+ Currently it works only with **oracle_enahanced** adapter.
9
+
10
+ ## Installation
11
+
12
+ Include the gem in your Gemfile:
13
+
14
+ gem "database_loader"
15
+
16
+ In Rails 2.x you also need to add this to you Rakefile
17
+
18
+ require "database_loader/tasks"
19
+
20
+ ## Structure
21
+
22
+ rails_app/
23
+ db/
24
+ migrate/
25
+ sql/
26
+ local/
27
+ views/
28
+ report_v.sql
29
+ ext/
30
+ packages/
31
+ sync_api.sql
32
+ views/
33
+ users_v.sql
34
+ materialized_views/
35
+ indexes/
36
+ functions/
37
+ scripts/
38
+ grants/
39
+
40
+ ## Load generated SQL into database
41
+
42
+ rake db:sql:load:local
43
+ rake db:sql:load:ext
44
+
45
+ ## Preview generated SQL
46
+
47
+ rake db:sql:dump:local
48
+ rake db:sql:dump:ext
49
+
50
+ ## Packaging a new release
51
+
52
+ In case you cannot directly access your external database,
53
+ you can generate a package containing all your SQL files and send it to DBA.
54
+
55
+ rake db:sql:package:local NAME="v1"
56
+ rake db:sql:package:ext NAME="v1"
57
+
58
+ ## Configuration
59
+
60
+ config/initializers/database_loader.rb
61
+
62
+ # Types that contain SQL files.
63
+ DatabaseLoader.types = [ :views, :materialized_views, :indexes, :packages, :functions, :scripts, :grants ]
64
+
65
+ # Use :erb, :erubis or :liquid to render SQL files.
66
+ DatabaseLoader.template_engine = :erb
67
+
68
+ # Path to store generated tar packages.
69
+ DatabaseLoader.package_path = "/tmp"
70
+
71
+ # Path to SH template that will be used to generate deployement script (see examples/template.sh)
72
+ DatabaseLoader.template_path = Rails.root.join("db", "sql", "template.sh")
73
+
74
+ ## Setting up database connections
75
+
76
+ Convention is to create additional configurations for
77
+ each environment and append the external schema name.
78
+
79
+ config/database.yml
80
+
81
+ development:
82
+ username: local
83
+ password: ...
84
+
85
+ development_ext:
86
+ username: external
87
+ password: ...
88
+
89
+
90
+ ## TODO
91
+
92
+ * SQL dependency handling.
93
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "database_loader/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "database_loader"
7
+ s.version = DatabaseLoader::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Edgars Beigarts"]
10
+ s.email = ["1@wb4.lv"]
11
+ s.homepage = "http://github.com/ebeigarts/database_loader"
12
+ s.description = %q{Load SQL views, materialized views, grants, etc. into database}
13
+ s.summary = s.description
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ echo "usage: $0 username password"
3
+
4
+ MYDIR=`pwd`
5
+
6
+ for sql_dir in views materialized_views packages indexes scripts do
7
+ do
8
+ if test -r ${MYDIR}/${sql_dir}
9
+ then
10
+ for sql_file in ${MYDIR}/${sql_dir}/*
11
+ do
12
+ if test -r ${sql_file}
13
+ then
14
+ echo create ${sql_dir} ${sql_file}
15
+ sqlplus -s $1/$1 @${sql_file}
16
+ fi
17
+ done
18
+ fi
19
+ done
@@ -0,0 +1,8 @@
1
+ module DatabaseLoader
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ DatabaseLoader.set_schemas
5
+ require "database_loader/tasks"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ module DatabaseLoader
2
+ class RubyFile < SqlFile
3
+ def render
4
+ "-- Cannot dump ruby file"
5
+ end
6
+ alias_method :to_s, :render
7
+
8
+ def excerpt
9
+ "#{read.first(60)} ..."
10
+ end
11
+
12
+ def statements
13
+ [self]
14
+ end
15
+
16
+ def execute
17
+ content = read
18
+ ActiveRecord::Schema.define do
19
+ instance_eval(content)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module DatabaseLoader
2
+ class SqlFile
3
+ attr_accessor :path, :username
4
+
5
+ def initialize(path)
6
+ self.path = path
7
+ end
8
+
9
+ def type
10
+ path.split(/[\/\\]/)[-2].to_sym
11
+ end
12
+
13
+ def name
14
+ File.basename(path)
15
+ end
16
+
17
+ def read
18
+ File.read(path)
19
+ end
20
+
21
+ def render
22
+ Template.new(read).render("username" => username)
23
+ end
24
+ alias_method :to_s, :render
25
+
26
+ def statements
27
+ render.split(/\r?\n\/\r?\n/).reject(&:blank?).map do |str|
28
+ SqlStatement.new(str)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ module DatabaseLoader
2
+ class SqlStatement
3
+ attr_accessor :contents
4
+
5
+ def initialize(contents)
6
+ self.contents = contents
7
+ end
8
+
9
+ def excerpt
10
+ text = contents.gsub(/\-\-[^\r\n]*/, "").squish
11
+ if text.size > 60
12
+ "#{text.first(60)} ..."
13
+ else
14
+ text
15
+ end
16
+ end
17
+
18
+ def execute
19
+ ActiveRecord::Base.connection.execute(contents)
20
+ end
21
+
22
+ def to_s
23
+ contents
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,136 @@
1
+ require "database_loader"
2
+
3
+ namespace :db do
4
+ namespace :sql do
5
+ namespace :load do
6
+ DatabaseLoader.schemas.each do |schema|
7
+ desc "Load SQL statements in #{schema}"
8
+ task schema do
9
+ DatabaseLoader.types.each do |type|
10
+ Rake::Task["db:sql:load:#{schema}:#{type}"].invoke
11
+ end
12
+ end
13
+ namespace schema do
14
+ DatabaseLoader.types.each do |type|
15
+ desc "Load SQL #{type.to_s.singularize} statements in #{schema}"
16
+ task type => :environment do
17
+ DatabaseLoader.connect_as(schema) do |connection_config|
18
+ DatabaseLoader.enable_dbms_output
19
+ DatabaseLoader.files(schema, type, ENV['NAME']).each do |file|
20
+ puts "* #{file.type}/#{file.name} ".ljust(80, "-")
21
+ file.username = connection_config[:username]
22
+ file.statements.each do |statement|
23
+ puts " #{statement.excerpt}"
24
+ begin
25
+ statement.execute
26
+ DatabaseLoader.dbms_output do |line|
27
+ puts "! DBMS_OUTPUT: #{line}"
28
+ end
29
+ rescue => e
30
+ $stderr.puts e.message
31
+ end
32
+ end
33
+ end
34
+ DatabaseLoader.invalid_objects.each do |h|
35
+ puts "! #{h['name']} #{h['type']} #{h['status']}"
36
+ end
37
+ DatabaseLoader.errors.each do |h|
38
+ puts "! #{h['name']} #{h['type']} #{h['attribute']} at line #{h['line']}: #{h['text']}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ namespace :dump do
48
+ DatabaseLoader.schemas.each do |schema|
49
+ desc "Dump SQL statements in #{schema}"
50
+ task schema do
51
+ DatabaseLoader.types.each do |type|
52
+ Rake::Task["db:sql:dump:#{schema}:#{type}"].invoke
53
+ end
54
+ end
55
+ namespace schema do
56
+ DatabaseLoader.types.each do |type|
57
+ desc "Dump SQL #{type.to_s.singularize} statements in #{schema}"
58
+ task type => :environment do
59
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
60
+ DatabaseLoader.files(schema, type, ENV['NAME']).each do |file|
61
+ file.username = connection_config[:username]
62
+ puts "-- #{file.type}/#{file.name} ".ljust(80, "-")
63
+ puts file.to_s
64
+ puts
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ namespace :package do
73
+ DatabaseLoader.schemas.each do |schema|
74
+ desc "Package SQL files for remote installation in #{schema}. rake db:sql:package:#{schema} NAME=CM0000072322 COMMIT=master."
75
+ task schema => :environment do
76
+ unless ENV['NAME'].present?
77
+ STDERR.puts "You have to specify NAME environment"
78
+ end
79
+ if ENV['COMMIT']
80
+ files = `git diff --diff-filter=ACM --name-only #{ENV['COMMIT']} ./db/sql/#{schema}`.strip.split(/\n/).sort
81
+ else
82
+ files = `find ./db/sql/#{schema} -type f`.strip.split("\n").sort
83
+ end
84
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
85
+ pkg_name = ENV['NAME']
86
+ pkg_dir = File.join(DatabaseLoader.package_path, connection_config[:username].to_s.downcase)
87
+ # Package SQL files
88
+ mkdir_p(File.join(pkg_dir, pkg_name))
89
+ Dir[File.join(pkg_dir, pkg_name) + "/*/"].each do |dir|
90
+ rm_rf(dir)
91
+ end
92
+ DatabaseLoader.types.each do |type|
93
+ DatabaseLoader.files(schema, type, "*").each do |file|
94
+ next unless files.any? { |f| f.include?(file.name) }
95
+ file.username = connection_config[:username]
96
+ puts "-- #{file.type}/#{file.name} ".ljust(80, "-")
97
+ sql = file.to_s
98
+ sql += "\nEXIT\n" # tell sqlplus to exit
99
+ # puts sql
100
+ FileUtils.mkdir_p(File.join(pkg_dir, pkg_name, file.type.to_s))
101
+ File.open(File.join(pkg_dir, pkg_name, file.type.to_s, file.name), "wb") { |f| f.write sql }
102
+ end
103
+ end
104
+ # Generate shell script
105
+ sh_content = DatabaseLoader::Template.new(File.read(DatabaseLoader.template_path)).render({
106
+ "cm_number" => pkg_name.gsub(/[^\d]/, ''),
107
+ "application" => connection_config[:username]
108
+ })
109
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.sh"), "wb") { |f| f.write(sh_content) }
110
+ # Generate summary
111
+ if ENV['COMMIT']
112
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.txt"), "wb") do |f|
113
+ f.write(`git diff --stat #{ENV['COMMIT']} ./db/sql/#{schema}`)
114
+ end
115
+ File.open(File.join(pkg_dir, pkg_name, "#{pkg_name}.diff"), "wb") do |f|
116
+ f.write(`git diff #{ENV['COMMIT']} ./db/sql/#{schema}`)
117
+ end
118
+ end
119
+ # Try to open the folder for manual reviewing
120
+ system `open #{File.join(pkg_dir, pkg_name)}`
121
+ # Cleanup & compress
122
+ puts "Press any key to continue"; STDIN.getc
123
+ Dir.chdir(pkg_dir)
124
+ sh "tar cf #{pkg_name}.tar #{pkg_name}"
125
+ ### print
126
+ puts
127
+ # puts "# export TWO_TASK=..."
128
+ puts "Instructions for #{pkg_name}:"
129
+ puts "$ tar vxf #{pkg_name}.tar"
130
+ puts "$ cd #{pkg_name}"
131
+ puts "$ sh #{pkg_name}.sh <#{connection_config[:username]}_user> <#{connection_config[:username]}_pwd> <#{schema}_user> <#{schema}_pwd>"
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,27 @@
1
+ module DatabaseLoader
2
+ class Template
3
+ attr_accessor :contents
4
+
5
+ def initialize(contents)
6
+ self.contents = contents
7
+ end
8
+
9
+ def render(options = {})
10
+ case DatabaseLoader.template_engine
11
+ when :liquid
12
+ require "liquid"
13
+ Liquid::Template.parse(contents).render(options)
14
+ when :erb
15
+ require "erb"
16
+ struct = OpenStruct.new(options)
17
+ ERB.new(contents).result(struct.send(:binding))
18
+ when :erubis
19
+ require "erubis"
20
+ struct = OpenStruct.new(options)
21
+ Erubis::Eruby.new(contents).result(struct.send(:binding))
22
+ else
23
+ contents
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module DatabaseLoader
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,123 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require "tmpdir"
4
+ require "ostruct"
5
+
6
+ require "database_loader/version"
7
+
8
+ module DatabaseLoader
9
+ def self.set_schemas
10
+ if Rails.root
11
+ self.schemas = Dir[File.expand_path("#{Rails.root}/db/sql/*")].select { |d| File.directory?(d) }.map { |d| File.basename(d).to_sym }
12
+ else
13
+ self.schemas = [:local]
14
+ end
15
+ end
16
+
17
+ # Schemas that contain SQL files. :local will be used to load in SQL files in current database.
18
+ mattr_accessor :schemas
19
+ self.set_schemas
20
+
21
+ # Folders that contain SQL files.
22
+ mattr_accessor :types
23
+ self.types = [ :views, :materialized_views, :indexes, :packages, :functions, :scripts, :grants ]
24
+
25
+ # Use :erb, :erubis or :liquid to render SQL files.
26
+ mattr_accessor :template_engine
27
+ self.template_engine = :erb
28
+
29
+ # Path to store generated deployement packages.
30
+ mattr_accessor :package_path
31
+ self.package_path = Dir.tmpdir
32
+
33
+ # Path to SH template that will be used to generate deployement script.
34
+ mattr_accessor :template_path
35
+ self.template_path = File.dirname(__FILE__) + "/../../examples"
36
+
37
+ def self.files(schema, type = nil, name = nil)
38
+ type ||= "**"
39
+ name ||= "*"
40
+ files = []
41
+ Dir[File.expand_path("#{Rails.root}/db/sql/#{schema}/#{type}")].each do |dir_path|
42
+ if DatabaseLoader.types.include?(File.basename(dir_path).to_sym)
43
+ Dir["#{dir_path}/#{name}.sql"].sort.each do |path|
44
+ files << SqlFile.new(path)
45
+ end
46
+ Dir["#{dir_path}/#{name}.rb"].sort.each do |path|
47
+ files << RubyFile.new(path)
48
+ end
49
+ end
50
+ end
51
+ files
52
+ end
53
+
54
+ def self.invalid_objects
55
+ ActiveRecord::Base.connection.select_all(%{
56
+ SELECT object_name AS name, object_type AS type, status
57
+ FROM user_objects
58
+ WHERE object_name LIKE '#{ActiveRecord::Base.table_name_prefix[0..-2].upcase}%'
59
+ AND status = 'INVALID'
60
+ AND object_type like 'PACKAGE%'
61
+ })
62
+ end
63
+
64
+ def self.errors
65
+ ActiveRecord::Base.connection.select_all(%{
66
+ SELECT *
67
+ FROM USER_ERRORS
68
+ WHERE name LIKE '#{ActiveRecord::Base.table_name_prefix[0..-2].upcase}%'
69
+ ORDER BY name, type, sequence
70
+ })
71
+ end
72
+
73
+ def self.enable_dbms_output
74
+ plsql.dbms_output.enable(10_000)
75
+ end
76
+
77
+ def self.dbms_output
78
+ loop do
79
+ result = plsql.dbms_output.get_line(:line => '', :status => 0)
80
+ break unless result[:status] == 0
81
+ yield result[:line]
82
+ end
83
+ end
84
+
85
+ def self.connect_as(schema)
86
+ begin
87
+ connection_config = ActiveRecord::Base.connection.raw_connection.instance_variable_get("@config").symbolize_keys
88
+ connection_name = nil
89
+ # When running rake test|spec RAILS_ENV is development by default,
90
+ # so we need to guess config from current connection.
91
+ ActiveRecord::Base.configurations.each do |name, config|
92
+ # current configuration?
93
+ if connection_config.symbolize_keys == config.symbolize_keys
94
+ # current configuration has also _apps configuration?
95
+ if ActiveRecord::Base.configurations.any? { |other_name, _| other_name.to_s == "#{name}_#{schema}" }
96
+ connection_name = name
97
+ break
98
+ end
99
+ end
100
+ end
101
+ if schema != :local
102
+ unless connection_name
103
+ raise "Missing database.yml configuration for <RAILS_ENV>_#{schema}."
104
+ end
105
+ puts "* Loading SQL statements in #{connection_name.downcase}_apps"
106
+ ActiveRecord::Base.establish_connection("#{connection_name}_#{schema}".to_sym)
107
+ end
108
+ yield connection_config
109
+ ensure
110
+ if schema != :local
111
+ # Set back the original connection
112
+ ActiveRecord::Base.establish_connection(connection_name)
113
+ end
114
+ end
115
+ end
116
+
117
+ autoload :SqlFile, 'database_loader/sql_file'
118
+ autoload :RubyFile, 'database_loader/ruby_file'
119
+ autoload :SqlStatement, 'database_loader/sql_statement'
120
+ autoload :Template, 'database_loader/template'
121
+ end
122
+
123
+ require "database_loader/railtie" if defined?(::Rails::Railtie)
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_loader
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Edgars Beigarts
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-18 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Load SQL views, materialized views, grants, etc. into database
23
+ email:
24
+ - 1@wb4.lv
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - database_loader.gemspec
37
+ - examples/template.sh
38
+ - lib/database_loader.rb
39
+ - lib/database_loader/railtie.rb
40
+ - lib/database_loader/ruby_file.rb
41
+ - lib/database_loader/sql_file.rb
42
+ - lib/database_loader/sql_statement.rb
43
+ - lib/database_loader/tasks.rb
44
+ - lib/database_loader/template.rb
45
+ - lib/database_loader/version.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/ebeigarts/database_loader
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.4.1
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Load SQL views, materialized views, grants, etc. into database
80
+ test_files: []
81
+