Skip to content

Commit edf52fd

Browse files
committed
[DB2] database tasks - with a tested (and fixed) structure_dump
1 parent 74a13ba commit edf52fd

File tree

6 files changed

+228
-79
lines changed

6 files changed

+228
-79
lines changed

lib/arjdbc/db2/adapter.rb

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -595,84 +595,6 @@ def drop_database(name = nil)
595595
tables.each { |table| drop_table("#{table}") }
596596
end
597597

598-
# TODO this seems like it's ready for quite some refactoring, anyone pls :) ?!
599-
def structure_dump # :nodoc:
600-
schema_name = schema.upcase if schema
601-
definition = ''
602-
for table in tables
603-
definition << "CREATE TABLE #{table} (\n"
604-
605-
cols_rs = jdbc_connection.meta_data.getColumns(nil, schema_name, table, nil)
606-
begin
607-
first_col = true
608-
while cols_rs.next
609-
col_name = add_quotes(cols_rs.getString(4))
610-
default = cols_rs.getString(13)
611-
default = default.empty? ? "" : " DEFAULT #{default}"
612-
613-
type = cols_rs.getString(6)
614-
col_precision = cols_rs.getString(7)
615-
col_scale = cols_rs.getString(9)
616-
col_size = ""
617-
if HAVE_SCALE.include?(type) and col_scale
618-
col_size = "(#{col_precision},#{col_scale})"
619-
elsif (HAVE_LIMIT + HAVE_PRECISION).include?(type) and col_precision
620-
col_size = "(#{col_precision})"
621-
end
622-
nulling = (cols_rs.getString(18) == 'NO' ? " NOT NULL" : "")
623-
autoinc = (cols_rs.getString(23) == 'YES' ? " GENERATED ALWAYS AS IDENTITY" : "")
624-
create_column = add_quotes(expand_double_quotes(strip_quotes(col_name))) <<
625-
" " << type << col_size << "" << nulling << default << autoinc
626-
create_column = first_col ? " #{create_column}" : ",\n #{create_column}"
627-
628-
definition << create_column
629-
630-
first_col = false
631-
end
632-
ensure
633-
cols_rs.close
634-
end
635-
636-
definition << ");\n\n"
637-
638-
pk_rs = jdbc_connection.meta_data.getPrimaryKeys(nil, schema_name, table)
639-
primary_key = {}
640-
begin
641-
while pk_rs.next
642-
name = pk_rs.getString(6)
643-
primary_key[name] = [] unless primary_key[name]
644-
primary_key[name] << pk_rs.getString(4)
645-
end
646-
ensure
647-
pk_rs.close
648-
end
649-
primary_key.each do |name, cols|
650-
definition << "ALTER TABLE #{table}\n"
651-
definition << " ADD CONSTRAINT #{name}\n"
652-
definition << " PRIMARY KEY (#{cols.join(', ')});\n\n"
653-
end
654-
end
655-
definition
656-
end
657-
658-
def add_quotes(name)
659-
name ? %Q{"#{name}"} : name
660-
end
661-
private :add_quotes
662-
663-
def strip_quotes(str)
664-
return str unless str
665-
return str unless /^(["']).*\1$/ =~ str
666-
str[1..-2]
667-
end
668-
private :strip_quotes
669-
670-
def expand_double_quotes(name)
671-
return name unless name && name['"']
672-
name.gsub(/"/,'""')
673-
end
674-
private :expand_double_quotes
675-
676598
def execute_table_change(sql, table_name, name = nil)
677599
outcome = execute(sql, name)
678600
reorg_table(table_name, name)

lib/arjdbc/tasks/database_tasks.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def self.tasks_instance(config)
2424
end
2525

2626
require 'arjdbc/tasks/jdbc_database_tasks'
27+
require 'arjdbc/tasks/db2_database_tasks'
2728
require 'arjdbc/tasks/derby_database_tasks'
2829
require 'arjdbc/tasks/h2_database_tasks'
2930
require 'arjdbc/tasks/hsqldb_database_tasks'
@@ -34,6 +35,7 @@ def self.tasks_instance(config)
3435
register_tasks(/(mssql|sqlserver)/i, MSSQLDatabaseTasks)
3536
register_tasks(/(oci|oracle)/i, OracleDatabaseTasks)
3637
# tasks for custom (JDBC) adapters :
38+
register_tasks(/db2/i, DB2DatabaseTasks)
3739
register_tasks(/derby/i, DerbyDatabaseTasks)
3840
register_tasks(/h2/i, H2DatabaseTasks)
3941
register_tasks(/hsqldb/i, HSQLDBDatabaseTasks)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require 'arjdbc/tasks/jdbc_database_tasks'
2+
3+
module ArJdbc
4+
module Tasks
5+
class DB2DatabaseTasks < JdbcDatabaseTasks
6+
7+
def create
8+
raise "AR-JDBC adapter 'DB2' does not support create_database"
9+
end
10+
11+
def purge
12+
establish_connection(config)
13+
connection.recreate_database
14+
end
15+
16+
# NOTE: does not work correctly (on non AS400) due driver meta data issue
17+
#
18+
# also try db2move e.g. `db2move SAMPLE EXPORT -sn db2inst`
19+
# - where SAMPLE is the database name
20+
# - and -sn specified schema name
21+
#
22+
23+
def structure_dump(filename)
24+
establish_connection(config)
25+
dump = File.open(filename, "w:utf-8")
26+
27+
schema_name = connection.schema.upcase if connection.schema
28+
meta_data = connection.jdbc_connection.meta_data
29+
tables_rs = meta_data.getTables(nil, schema_name, nil, ["TABLE"].to_java(:String))
30+
31+
have_scale = ArJdbc::DB2::HAVE_SCALE
32+
have_precision = ArJdbc::DB2::HAVE_LIMIT + ArJdbc::DB2::HAVE_LIMIT
33+
34+
while tables_rs.next
35+
table_name = tables_rs.getString('TABLE_NAME')
36+
dump << "CREATE TABLE #{connection.quote_table_name(table_name)} (\n"
37+
38+
cols_rs = meta_data.getColumns(nil, schema_name, table_name, nil)
39+
begin
40+
first_col = true
41+
while cols_rs.next
42+
column_name = cols_rs.getString(4)
43+
default = cols_rs.getString(13)
44+
default = default.empty? ? "" : " DEFAULT #{default}" if default
45+
type = cols_rs.getString(6)
46+
precision, scale = cols_rs.getString(7), cols_rs.getString(9)
47+
column_size = ""
48+
if scale && have_scale.include?(type)
49+
column_size = "(#{precision},#{scale})"
50+
elsif precision && have_precision.include?(type)
51+
column_size = "(#{precision})"
52+
end
53+
nulling = ( cols_rs.getString(18) == 'NO' ? " NOT NULL" : nil )
54+
autoinc = ( cols_rs.getString(23) == 'YES' ? " GENERATED ALWAYS AS IDENTITY" : nil )
55+
56+
create_column = connection.quote_column_name(column_name)
57+
create_column << " #{type}"
58+
create_column << column_size
59+
create_column << nulling.to_s
60+
create_column << default.to_s
61+
create_column << autoinc.to_s
62+
63+
create_column = first_col ? " #{create_column}" : ",\n #{create_column}"
64+
dump << create_column
65+
66+
first_col = false
67+
end
68+
ensure
69+
cols_rs.close
70+
end
71+
72+
dump << "\n);\n\n"
73+
74+
pk_rs = meta_data.getPrimaryKeys(nil, schema_name, table_name)
75+
primary_keys = {}
76+
begin
77+
while pk_rs.next
78+
name = pk_rs.getString(6)
79+
primary_keys[name] ||= []
80+
primary_keys[name] << pk_rs.getString(4)
81+
end
82+
ensure
83+
pk_rs.close
84+
end
85+
primary_keys.each do |name, cols|
86+
dump << "ALTER TABLE #{connection.quote_table_name(table_name)}\n"
87+
dump << " ADD CONSTRAINT #{name}\n"
88+
dump << " PRIMARY KEY (#{cols.join(', ')});\n\n"
89+
end
90+
end
91+
92+
dump.close
93+
end
94+
95+
def structure_load(filename)
96+
establish_connection(config)
97+
IO.read(filename).split(/;\n*/m).each do |ddl|
98+
connection.execute ddl.sub(/;$/, '')
99+
end
100+
end
101+
102+
end
103+
end
104+
end

test/db/db2/rake_test.rb

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
require 'rake_test_support'
2+
require 'db/db2'
3+
4+
class DB2RakeDbCreateTest < Test::Unit::TestCase
5+
include RakeTestSupport
6+
7+
def db_name; nil; end # using same DB (db:create is a bit IBM-plicated)
8+
9+
test 'rake db:test:purge' do
10+
# Rake::Task["db:create"].invoke
11+
create_rake_test_database do |connection|
12+
create_schema_migrations_table(connection)
13+
connection.create_table('ibmers_test') { |t| t.string :name }
14+
end
15+
16+
Rake::Task["db:test:purge"].invoke
17+
18+
establish_test_connection
19+
assert_false ActiveRecord::Base.connection.table_exists?('ibmers_test')
20+
ActiveRecord::Base.connection.disconnect!
21+
end
22+
23+
test 'rake db:structure:dump (and db:structure:load)' do
24+
# Rake::Task["db:create"].invoke
25+
create_rake_test_database do |connection|
26+
drop_all_database_tables(connection)
27+
create_sample_database_data(connection)
28+
create_schema_migrations_table(connection)
29+
connection.create_table('ibmers') { |t| t.string :name; t.timestamps }
30+
end
31+
32+
structure_sql = File.join('db', structure_sql_filename)
33+
begin
34+
Dir.mkdir 'db' # db/structure.sql
35+
Rake::Task["db:structure:dump"].invoke
36+
37+
assert File.exists?(structure_sql)
38+
assert_match /CREATE TABLE ibmers/im, File.read(structure_sql)
39+
40+
# db:structure:load
41+
drop_all_database_tables
42+
create_rake_test_database
43+
Rake::Task["db:structure:load"].invoke
44+
45+
establish_test_connection
46+
assert ActiveRecord::Base.connection.table_exists?('ibmers')
47+
ActiveRecord::Base.connection.disconnect!
48+
ensure
49+
File.delete(structure_sql) if File.exists?(structure_sql)
50+
Dir.rmdir 'db'
51+
end
52+
end
53+
54+
setup { rm_r 'db' if File.exist?('db') }
55+
56+
private
57+
58+
def establish_test_connection
59+
config = db_config.dup
60+
config.merge! :database => db_name if db_name
61+
ActiveRecord::Base.establish_connection config
62+
end
63+
64+
def create_sample_database_data(connection)
65+
filename = File.expand_path('rake_test_data.sql', File.dirname(__FILE__))
66+
File.read(filename).split(/;\n\n/).each { |ddl| connection.execute(ddl) }
67+
end
68+
69+
def drop_all_database_tables(connection = nil)
70+
established = nil
71+
connection ||= begin
72+
ActiveRecord::Base.connection
73+
rescue
74+
ActiveRecord::Base.establish_connection db_config
75+
established = true
76+
ActiveRecord::Base.connection
77+
end
78+
connection.tables.each { |table| connection.drop_table(table) }
79+
ActiveRecord::Base.connection.disconnect! if established
80+
end
81+
82+
end

test/db/db2/rake_test_data.sql

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
CREATE TABLE EMPLOYEE (
2+
EMPNO CHAR(6) NOT NULL,
3+
FIRSTNME VARCHAR(12) NOT NULL,
4+
MIDINIT CHAR(1),
5+
LASTNAME VARCHAR(15) NOT NULL,
6+
WORKDEPT CHAR(3),
7+
PHONENO CHAR(4),
8+
HIREDATE DATE,
9+
JOB CHAR(8),
10+
EDLEVEL SMALLINT NOT NULL,
11+
SEX CHAR(1),
12+
BIRTHDATE DATE,
13+
SALARY DECIMAL(9,2),
14+
BONUS DECIMAL(9,2),
15+
COMM DECIMAL(9,2)
16+
);
17+
18+
ALTER TABLE EMPLOYEE ADD CONSTRAINT PK_EMPLOYEE PRIMARY KEY (EMPNO);
19+
20+
CREATE TABLE EMP_PHOTO (
21+
EMPNO CHAR(6) NOT NULL,
22+
PHOTO_FORMAT VARCHAR(10) NOT NULL,
23+
PICTURE BLOB(102400)
24+
);
25+
26+
ALTER TABLE EMP_PHOTO ADD CONSTRAINT PK_EMP_PHOTO PRIMARY KEY (EMPNO, PHOTO_FORMAT);
27+
28+
CREATE TABLE EMP_RESUME (
29+
EMPNO CHAR(6) NOT NULL,
30+
RESUME_FORMAT VARCHAR(10) NOT NULL,
31+
RESUME CLOB(5120)
32+
);
33+
34+
ALTER TABLE EMP_RESUME ADD CONSTRAINT PK_EMP_RESUME PRIMARY KEY (EMPNO, RESUME_FORMAT);
35+

test/rake_test_support.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def new_application
116116

117117
def create_schema_migrations_table(connection = ActiveRecord::Base.connection)
118118
schema_migration = ActiveRecord::Migrator.schema_migrations_table_name
119+
return if connection.table_exists?(schema_migration)
119120
connection.create_table(schema_migration, :id => false) do |t|
120121
t.column :version, :string, :null => false
121122
end
@@ -126,7 +127,10 @@ def create_rake_test_database
126127
connection = ActiveRecord::Base.connection
127128
connection.create_database(db_name, db_config) if connection.respond_to?(:create_database)
128129
if block_given?
129-
ActiveRecord::Base.establish_connection db_config.merge :database => db_name
130+
if db_name
131+
config = db_config.merge :database => db_name
132+
ActiveRecord::Base.establish_connection config
133+
end
130134
yield ActiveRecord::Base.connection
131135
end
132136
ActiveRecord::Base.connection.disconnect!

0 commit comments

Comments
 (0)