activerecord-oracle_enhanced-adapter 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +17 -0
- data/License.txt +1 -1
- data/README.rdoc +3 -1
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +6 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +333 -109
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +60 -36
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +13 -18
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +3 -4
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +3 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +86 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +31 -2
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +267 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +6 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +22 -75
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +61 -0
- data/spec/spec_helper.rb +3 -15
- metadata +4 -2
@@ -23,14 +23,7 @@ begin
|
|
23
23
|
|
24
24
|
rescue LoadError, NameError
|
25
25
|
# JDBC driver is unavailable.
|
26
|
-
|
27
|
-
"Please install ojdbc14.jar library."
|
28
|
-
if defined?(RAILS_DEFAULT_LOGGER)
|
29
|
-
RAILS_DEFAULT_LOGGER.error error_message
|
30
|
-
else
|
31
|
-
STDERR.puts error_message
|
32
|
-
end
|
33
|
-
raise LoadError
|
26
|
+
raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install ojdbc14.jar library."
|
34
27
|
end
|
35
28
|
|
36
29
|
|
@@ -53,46 +46,77 @@ module ActiveRecord
|
|
53
46
|
new_connection(@config)
|
54
47
|
end
|
55
48
|
|
49
|
+
# modified method to support JNDI connections
|
56
50
|
def new_connection(config)
|
57
|
-
username
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
51
|
+
username = nil
|
52
|
+
|
53
|
+
if config[:jndi]
|
54
|
+
jndi = config[:jndi].to_s
|
55
|
+
ctx = javax.naming.InitialContext.new
|
56
|
+
ds = nil
|
57
|
+
|
58
|
+
# tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
|
59
|
+
begin
|
60
|
+
env = ctx.lookup('java:/comp/env')
|
61
|
+
ds = env.lookup(jndi)
|
62
|
+
rescue
|
63
|
+
ds = ctx.lookup(jndi)
|
64
|
+
end
|
65
|
+
|
66
|
+
# check if datasource supports pooled connections, otherwise use default
|
67
|
+
if ds.respond_to?(:pooled_connection)
|
68
|
+
@raw_connection = ds.pooled_connection
|
69
|
+
else
|
70
|
+
@raw_connection = ds.connection
|
71
|
+
end
|
72
|
+
|
73
|
+
config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
|
74
|
+
username = @raw_connection.meta_data.user_name
|
64
75
|
else
|
65
|
-
|
76
|
+
username = config[:username].to_s
|
77
|
+
password, database = config[:password].to_s, config[:database].to_s
|
78
|
+
privilege = config[:privilege] && config[:privilege].to_s
|
79
|
+
host, port = config[:host], config[:port]
|
80
|
+
|
81
|
+
# connection using TNS alias
|
82
|
+
if database && !host && !config[:url] && ENV['TNS_ADMIN']
|
83
|
+
url = "jdbc:oracle:thin:@#{database || 'XE'}"
|
84
|
+
else
|
85
|
+
url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}:#{database || 'XE'}"
|
86
|
+
end
|
87
|
+
|
88
|
+
prefetch_rows = config[:prefetch_rows] || 100
|
89
|
+
# get session time_zone from configuration or from TZ environment variable
|
90
|
+
time_zone = config[:time_zone] || ENV['TZ'] || java.util.TimeZone.default.getID
|
91
|
+
|
92
|
+
properties = java.util.Properties.new
|
93
|
+
properties.put("user", username)
|
94
|
+
properties.put("password", password)
|
95
|
+
properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
|
96
|
+
properties.put("internal_logon", privilege) if privilege
|
97
|
+
|
98
|
+
@raw_connection = java.sql.DriverManager.getConnection(url, properties)
|
99
|
+
|
100
|
+
# Set session time zone to current time zone
|
101
|
+
@raw_connection.setSessionTimeZone(time_zone)
|
102
|
+
|
103
|
+
# Set default number of rows to prefetch
|
104
|
+
# @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
|
66
105
|
end
|
67
106
|
|
68
|
-
prefetch_rows = config[:prefetch_rows] || 100
|
69
|
-
cursor_sharing = config[:cursor_sharing] || 'force'
|
70
107
|
# by default VARCHAR2 column size will be interpreted as max number of characters (and not bytes)
|
71
108
|
nls_length_semantics = config[:nls_length_semantics] || 'CHAR'
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
properties = java.util.Properties.new
|
76
|
-
properties.put("user", username)
|
77
|
-
properties.put("password", password)
|
78
|
-
properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
|
79
|
-
properties.put("internal_logon", privilege) if privilege
|
80
|
-
|
81
|
-
@raw_connection = java.sql.DriverManager.getConnection(url, properties)
|
109
|
+
cursor_sharing = config[:cursor_sharing] || 'force'
|
110
|
+
|
111
|
+
# from here it remaings common for both connections types
|
82
112
|
exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
|
83
113
|
exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS:FF6'}
|
84
114
|
exec "alter session set cursor_sharing = #{cursor_sharing}"
|
85
115
|
exec "alter session set nls_length_semantics = '#{nls_length_semantics}'"
|
86
116
|
self.autocommit = true
|
87
|
-
|
88
|
-
# Set session time zone to current time zone
|
89
|
-
@raw_connection.setSessionTimeZone(time_zone)
|
90
|
-
|
91
|
-
# Set default number of rows to prefetch
|
92
|
-
# @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
|
93
|
-
|
117
|
+
|
94
118
|
# default schema owner
|
95
|
-
@owner = username.upcase
|
119
|
+
@owner = username.upcase unless username.nil?
|
96
120
|
|
97
121
|
@raw_connection
|
98
122
|
end
|
@@ -1,24 +1,17 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
|
3
3
|
begin
|
4
|
-
require
|
5
|
-
|
6
|
-
# added mapping for TIMESTAMP / WITH TIME ZONE / LOCAL TIME ZONE types
|
7
|
-
# latest version of Ruby-OCI8 supports fractional seconds for timestamps
|
8
|
-
# therefore default binding to Time class should be used
|
9
|
-
# OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::OraDate
|
10
|
-
# OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_TZ] = OCI8::BindType::OraDate
|
11
|
-
# OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_LTZ] = OCI8::BindType::OraDate
|
4
|
+
require "oci8"
|
12
5
|
rescue LoadError
|
13
6
|
# OCI8 driver is unavailable.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
raise LoadError
|
7
|
+
raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load ruby-oci8 library. Please install ruby-oci8 gem."
|
8
|
+
end
|
9
|
+
|
10
|
+
# check ruby-oci8 version
|
11
|
+
required_oci8_version = [2, 0, 3]
|
12
|
+
oci8_version_ints = OCI8::VERSION.scan(/\d+/).map{|s| s.to_i}
|
13
|
+
if (oci8_version_ints <=> required_oci8_version) < 0
|
14
|
+
raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
|
22
15
|
end
|
23
16
|
|
24
17
|
module ActiveRecord
|
@@ -156,10 +149,11 @@ module ActiveRecord
|
|
156
149
|
value
|
157
150
|
when String
|
158
151
|
value
|
159
|
-
when Float
|
152
|
+
when Float, BigDecimal
|
153
|
+
# return Fixnum or Bignum if value is integer (to avoid issues with _before_type_cast values for id attributes)
|
160
154
|
value == (v_to_i = value.to_i) ? v_to_i : value
|
161
|
-
# ruby-oci8 2.0 returns OraNumber if Oracle type is NUMBER
|
162
155
|
when OraNumber
|
156
|
+
# change OraNumber value (returned in early versions of ruby-oci8 2.0.x) to BigDecimal
|
163
157
|
value == (v_to_i = value.to_i) ? v_to_i : BigDecimal.new(value.to_s)
|
164
158
|
when OCI8::LOB
|
165
159
|
if get_lob_value
|
@@ -209,6 +203,7 @@ module ActiveRecord
|
|
209
203
|
::DateTime.civil(year, month, day, hour, min, sec, offset)
|
210
204
|
end
|
211
205
|
end
|
206
|
+
|
212
207
|
end
|
213
208
|
|
214
209
|
# The OracleEnhancedOCIFactory factors out the code necessary to connect and
|
@@ -4,7 +4,6 @@ ActiveRecord::Base.class_eval do
|
|
4
4
|
class_inheritable_accessor :custom_create_method, :custom_update_method, :custom_delete_method
|
5
5
|
end
|
6
6
|
|
7
|
-
require 'ruby_plsql'
|
8
7
|
require 'active_support'
|
9
8
|
|
10
9
|
module ActiveRecord #:nodoc:
|
@@ -53,7 +52,7 @@ module ActiveRecord #:nodoc:
|
|
53
52
|
self.custom_delete_method = block
|
54
53
|
end
|
55
54
|
|
56
|
-
def create_method_name_before_custom_methods
|
55
|
+
def create_method_name_before_custom_methods #:nodoc:
|
57
56
|
if private_method_defined?(:create_without_timestamps) && defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING.to_f >= 2.3
|
58
57
|
:create_without_timestamps
|
59
58
|
elsif private_method_defined?(:create_without_callbacks)
|
@@ -63,7 +62,7 @@ module ActiveRecord #:nodoc:
|
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
66
|
-
def update_method_name_before_custom_methods
|
65
|
+
def update_method_name_before_custom_methods #:nodoc:
|
67
66
|
if private_method_defined?(:update_without_dirty)
|
68
67
|
:update_without_dirty
|
69
68
|
elsif private_method_defined?(:update_without_timestamps) && defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING.to_f >= 2.3
|
@@ -75,7 +74,7 @@ module ActiveRecord #:nodoc:
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
|
-
def destroy_method_name_before_custom_methods
|
77
|
+
def destroy_method_name_before_custom_methods #:nodoc:
|
79
78
|
if public_method_defined?(:destroy_without_callbacks)
|
80
79
|
:destroy_without_callbacks
|
81
80
|
else
|
@@ -6,6 +6,9 @@ module ActiveRecord
|
|
6
6
|
class OracleEnhancedSynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
|
7
7
|
end
|
8
8
|
|
9
|
+
class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :tablespace, :columns) #:nodoc:
|
10
|
+
end
|
11
|
+
|
9
12
|
module OracleEnhancedSchemaDefinitions #:nodoc:
|
10
13
|
def self.included(base)
|
11
14
|
base::TableDefinition.class_eval do
|
@@ -26,7 +26,7 @@ module ActiveRecord #:nodoc:
|
|
26
26
|
end
|
27
27
|
# change table name inspect method
|
28
28
|
tbl.extend TableInspect
|
29
|
-
|
29
|
+
oracle_enhanced_table(tbl, stream)
|
30
30
|
# add primary key trigger if table has it
|
31
31
|
primary_key_trigger(tbl, stream)
|
32
32
|
end
|
@@ -94,6 +94,7 @@ module ActiveRecord #:nodoc:
|
|
94
94
|
statment_parts << index.columns.inspect
|
95
95
|
statment_parts << (':name => ' + index.name.inspect)
|
96
96
|
statment_parts << ':unique => true' if index.unique
|
97
|
+
statment_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
|
97
98
|
|
98
99
|
' ' + statment_parts.join(', ')
|
99
100
|
end
|
@@ -103,6 +104,90 @@ module ActiveRecord #:nodoc:
|
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
107
|
+
def oracle_enhanced_table(table, stream)
|
108
|
+
columns = @connection.columns(table)
|
109
|
+
begin
|
110
|
+
tbl = StringIO.new
|
111
|
+
|
112
|
+
# first dump primary key column
|
113
|
+
if @connection.respond_to?(:pk_and_sequence_for)
|
114
|
+
pk, pk_seq = @connection.pk_and_sequence_for(table)
|
115
|
+
elsif @connection.respond_to?(:primary_key)
|
116
|
+
pk = @connection.primary_key(table)
|
117
|
+
end
|
118
|
+
|
119
|
+
tbl.print " create_table #{table.inspect}"
|
120
|
+
|
121
|
+
# addition to make temporary option work
|
122
|
+
tbl.print ", :temporary => true" if @connection.temporary_table?(table)
|
123
|
+
|
124
|
+
if columns.detect { |c| c.name == pk }
|
125
|
+
if pk != 'id'
|
126
|
+
tbl.print %Q(, :primary_key => "#{pk}")
|
127
|
+
end
|
128
|
+
else
|
129
|
+
tbl.print ", :id => false"
|
130
|
+
end
|
131
|
+
tbl.print ", :force => true"
|
132
|
+
tbl.puts " do |t|"
|
133
|
+
|
134
|
+
# then dump all non-primary key columns
|
135
|
+
column_specs = columns.map do |column|
|
136
|
+
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
137
|
+
next if column.name == pk
|
138
|
+
spec = {}
|
139
|
+
spec[:name] = column.name.inspect
|
140
|
+
spec[:type] = column.type.to_s
|
141
|
+
spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
|
142
|
+
spec[:precision] = column.precision.inspect if !column.precision.nil?
|
143
|
+
spec[:scale] = column.scale.inspect if !column.scale.nil?
|
144
|
+
spec[:null] = 'false' if !column.null
|
145
|
+
spec[:default] = default_string(column.default) if column.has_default?
|
146
|
+
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
|
147
|
+
spec
|
148
|
+
end.compact
|
149
|
+
|
150
|
+
# find all migration keys used in this table
|
151
|
+
keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
|
152
|
+
|
153
|
+
# figure out the lengths for each column based on above keys
|
154
|
+
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
|
155
|
+
|
156
|
+
# the string we're going to sprintf our values against, with standardized column widths
|
157
|
+
format_string = lengths.map{ |len| "%-#{len}s" }
|
158
|
+
|
159
|
+
# find the max length for the 'type' column, which is special
|
160
|
+
type_length = column_specs.map{ |column| column[:type].length }.max
|
161
|
+
|
162
|
+
# add column type definition to our format string
|
163
|
+
format_string.unshift " t.%-#{type_length}s "
|
164
|
+
|
165
|
+
format_string *= ''
|
166
|
+
|
167
|
+
column_specs.each do |colspec|
|
168
|
+
values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
|
169
|
+
values.unshift colspec[:type]
|
170
|
+
tbl.print((format_string % values).gsub(/,\s*$/, ''))
|
171
|
+
tbl.puts
|
172
|
+
end
|
173
|
+
|
174
|
+
tbl.puts " end"
|
175
|
+
tbl.puts
|
176
|
+
|
177
|
+
indexes(table, tbl)
|
178
|
+
|
179
|
+
tbl.rewind
|
180
|
+
stream.print tbl.read
|
181
|
+
rescue => e
|
182
|
+
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
183
|
+
stream.puts "# #{e.message}"
|
184
|
+
stream.puts
|
185
|
+
end
|
186
|
+
|
187
|
+
stream
|
188
|
+
end
|
189
|
+
|
190
|
+
|
106
191
|
# remove table name prefix and suffix when doing #inspect (which is used in tables method)
|
107
192
|
module TableInspect #:nodoc:
|
108
193
|
def inspect
|
@@ -249,18 +249,30 @@ describe "OracleEnhancedAdapter" do
|
|
249
249
|
describe "without composite_primary_keys" do
|
250
250
|
|
251
251
|
before(:all) do
|
252
|
+
@conn.execute "DROP TABLE test_employees" rescue nil
|
253
|
+
@conn.execute <<-SQL
|
254
|
+
CREATE TABLE test_employees (
|
255
|
+
employee_id NUMBER PRIMARY KEY,
|
256
|
+
name VARCHAR2(50)
|
257
|
+
)
|
258
|
+
SQL
|
252
259
|
Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
|
253
|
-
class ::
|
260
|
+
class ::TestEmployee < ActiveRecord::Base
|
254
261
|
set_primary_key :employee_id
|
255
262
|
end
|
256
263
|
end
|
257
264
|
|
265
|
+
after(:all) do
|
266
|
+
Object.send(:remove_const, "TestEmployee")
|
267
|
+
@conn.execute "DROP TABLE test_employees"
|
268
|
+
end
|
269
|
+
|
258
270
|
it "should tell ActiveRecord that count distinct is supported" do
|
259
271
|
ActiveRecord::Base.connection.supports_count_distinct?.should be_true
|
260
272
|
end
|
261
273
|
|
262
274
|
it "should execute correct SQL COUNT DISTINCT statement" do
|
263
|
-
lambda {
|
275
|
+
lambda { TestEmployee.count(:employee_id, :distinct => true) }.should_not raise_error
|
264
276
|
end
|
265
277
|
|
266
278
|
end
|
@@ -445,4 +457,21 @@ describe "OracleEnhancedAdapter" do
|
|
445
457
|
end
|
446
458
|
end
|
447
459
|
|
460
|
+
describe "temporary tables" do
|
461
|
+
|
462
|
+
after(:each) do
|
463
|
+
@conn.drop_table :foos rescue nil
|
464
|
+
end
|
465
|
+
it "should create ok" do
|
466
|
+
@conn.create_table :foos, :temporary => true, :id => false do |t|
|
467
|
+
t.integer :id
|
468
|
+
end
|
469
|
+
end
|
470
|
+
it "should show up as temporary" do
|
471
|
+
@conn.create_table :foos, :temporary => true, :id => false do |t|
|
472
|
+
t.integer :id
|
473
|
+
end
|
474
|
+
@conn.temporary_table?("foos").should be_true
|
475
|
+
end
|
476
|
+
end
|
448
477
|
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe "OracleEnhancedAdapter structure dump" do
|
4
|
+
include LoggerSpecHelper
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
8
|
+
@conn = ActiveRecord::Base.connection
|
9
|
+
end
|
10
|
+
describe "structure dump" do
|
11
|
+
before(:each) do
|
12
|
+
@conn.create_table :test_posts, :force => true do |t|
|
13
|
+
t.string :title
|
14
|
+
t.string :foo
|
15
|
+
t.integer :foo_id
|
16
|
+
end
|
17
|
+
@conn.create_table :foos do |t|
|
18
|
+
end
|
19
|
+
class ::TestPost < ActiveRecord::Base
|
20
|
+
end
|
21
|
+
TestPost.set_table_name "test_posts"
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:each) do
|
25
|
+
@conn.drop_table :test_posts
|
26
|
+
@conn.drop_table :foos
|
27
|
+
@conn.execute "DROP SEQUENCE test_posts_seq" rescue nil
|
28
|
+
@conn.execute "ALTER TABLE test_posts drop CONSTRAINT fk_test_post_foo" rescue nil
|
29
|
+
@conn.execute "DROP TRIGGER test_post_trigger" rescue nil
|
30
|
+
@conn.execute "DROP TYPE TEST_TYPE" rescue nil
|
31
|
+
@conn.execute "DROP TABLE bars" rescue nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should dump single primary key" do
|
35
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
36
|
+
dump.should =~ /CONSTRAINT (.+) PRIMARY KEY \(ID\)\n/
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should dump composite primary keys" do
|
40
|
+
pk = @conn.send(:select_one, <<-SQL)
|
41
|
+
select constraint_name from user_constraints where table_name = 'TEST_POSTS' and constraint_type='P'
|
42
|
+
SQL
|
43
|
+
@conn.execute <<-SQL
|
44
|
+
alter table test_posts drop constraint #{pk["constraint_name"]}
|
45
|
+
SQL
|
46
|
+
@conn.execute <<-SQL
|
47
|
+
ALTER TABLE TEST_POSTS
|
48
|
+
add CONSTRAINT pk_id_title PRIMARY KEY (id, title)
|
49
|
+
SQL
|
50
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
51
|
+
dump.should =~ /CONSTRAINT (.+) PRIMARY KEY \(ID,TITLE\)\n/
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should dump foreign keys" do
|
55
|
+
@conn.execute <<-SQL
|
56
|
+
ALTER TABLE TEST_POSTS
|
57
|
+
ADD CONSTRAINT fk_test_post_foo FOREIGN KEY (foo_id) REFERENCES foos(id)
|
58
|
+
SQL
|
59
|
+
dump = ActiveRecord::Base.connection.structure_dump_fk_constraints
|
60
|
+
dump.split('\n').length.should == 1
|
61
|
+
dump.should =~ /ALTER TABLE TEST_POSTS ADD CONSTRAINT fk_test_post_foo FOREIGN KEY \(foo_id\) REFERENCES foos\(id\)/
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not error when no foreign keys are present" do
|
65
|
+
dump = ActiveRecord::Base.connection.structure_dump_fk_constraints
|
66
|
+
dump.split('\n').length.should == 0
|
67
|
+
dump.should == ''
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should dump triggers" do
|
71
|
+
@conn.execute <<-SQL
|
72
|
+
create or replace TRIGGER TEST_POST_TRIGGER
|
73
|
+
BEFORE INSERT
|
74
|
+
ON TEST_POSTS
|
75
|
+
FOR EACH ROW
|
76
|
+
BEGIN
|
77
|
+
SELECT 'bar' INTO :new.FOO FROM DUAL;
|
78
|
+
END;
|
79
|
+
SQL
|
80
|
+
dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
|
81
|
+
dump.should =~ /create or replace TRIGGER TEST_POST_TRIGGER/
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should dump types" do
|
85
|
+
@conn.execute <<-SQL
|
86
|
+
create or replace TYPE TEST_TYPE AS TABLE OF VARCHAR2(10);
|
87
|
+
SQL
|
88
|
+
dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
|
89
|
+
dump.should =~ /create or replace TYPE TEST_TYPE/
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should dump virtual columns" do
|
93
|
+
pending "Not supported in this database version" unless @conn.select_value("SELECT * FROM v$version WHERE banner LIKE 'Oracle%11g%'")
|
94
|
+
@conn.execute <<-SQL
|
95
|
+
CREATE TABLE bars (
|
96
|
+
id NUMBER(38,0) NOT NULL,
|
97
|
+
id_plus NUMBER GENERATED ALWAYS AS(id + 2) VIRTUAL,
|
98
|
+
PRIMARY KEY (ID)
|
99
|
+
)
|
100
|
+
SQL
|
101
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
102
|
+
dump.should =~ /id_plus number GENERATED ALWAYS AS \(ID\+2\) VIRTUAL/
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should dump unique keys" do
|
106
|
+
@conn.execute <<-SQL
|
107
|
+
ALTER TABLE test_posts
|
108
|
+
add CONSTRAINT uk_foo_foo_id UNIQUE (foo, foo_id)
|
109
|
+
SQL
|
110
|
+
dump = ActiveRecord::Base.connection.structure_dump_unique_keys("test_posts")
|
111
|
+
dump.should == [" CONSTRAINT UK_FOO_FOO_ID UNIQUE (FOO,FOO_ID)"]
|
112
|
+
|
113
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
114
|
+
dump.should =~ /CONSTRAINT UK_FOO_FOO_ID UNIQUE \(FOO,FOO_ID\)/
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should dump indexes" do
|
118
|
+
ActiveRecord::Base.connection.add_index(:test_posts, :foo, :name => :ix_test_posts_foo)
|
119
|
+
ActiveRecord::Base.connection.add_index(:test_posts, :foo_id, :name => :ix_test_posts_foo_id, :unique => true)
|
120
|
+
|
121
|
+
@conn.execute <<-SQL
|
122
|
+
ALTER TABLE test_posts
|
123
|
+
add CONSTRAINT uk_foo_foo_id UNIQUE (foo, foo_id)
|
124
|
+
SQL
|
125
|
+
|
126
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
127
|
+
dump.should =~ /create unique index ix_test_posts_foo_id on test_posts \(foo_id\)/i
|
128
|
+
dump.should =~ /create index ix_test_posts_foo on test_posts \(foo\)/i
|
129
|
+
dump.should_not =~ /create unique index uk_test_posts_/i
|
130
|
+
end
|
131
|
+
end
|
132
|
+
describe "temporary tables" do
|
133
|
+
after(:all) do
|
134
|
+
@conn.drop_table :test_comments rescue nil
|
135
|
+
end
|
136
|
+
it "should dump correctly" do
|
137
|
+
@conn.create_table :test_comments, :temporary => true, :id => false do |t|
|
138
|
+
t.integer :post_id
|
139
|
+
end
|
140
|
+
dump = ActiveRecord::Base.connection.structure_dump
|
141
|
+
dump.should =~ /create global temporary table test_comments/i
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "database stucture dump extentions" do
|
146
|
+
before(:all) do
|
147
|
+
@conn.execute <<-SQL
|
148
|
+
CREATE TABLE nvarchartable (
|
149
|
+
unq_nvarchar NVARCHAR2(255) DEFAULT NULL
|
150
|
+
)
|
151
|
+
SQL
|
152
|
+
end
|
153
|
+
|
154
|
+
after(:all) do
|
155
|
+
@conn.execute "DROP TABLE nvarchartable"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return the character size of nvarchar fields" do
|
159
|
+
if /.*unq_nvarchar nvarchar2\((\d+)\).*/ =~ @conn.structure_dump
|
160
|
+
"#$1".should == "255"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "temp_table_drop" do
|
166
|
+
before(:each) do
|
167
|
+
@conn.create_table :temp_tbl, :temporary => true do |t|
|
168
|
+
t.string :foo
|
169
|
+
end
|
170
|
+
@conn.create_table :not_temp_tbl do |t|
|
171
|
+
t.string :foo
|
172
|
+
end
|
173
|
+
end
|
174
|
+
it "should dump drop sql for just temp tables" do
|
175
|
+
dump = @conn.temp_table_drop
|
176
|
+
dump.should =~ /drop table temp_tbl/i
|
177
|
+
dump.should_not =~ /drop table not_temp_tbl/i
|
178
|
+
end
|
179
|
+
after(:each) do
|
180
|
+
@conn.drop_table :temp_tbl
|
181
|
+
@conn.drop_table :not_temp_tbl
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "full drop" do
|
186
|
+
before(:each) do
|
187
|
+
@conn.create_table :full_drop_test do |t|
|
188
|
+
t.integer :id
|
189
|
+
end
|
190
|
+
@conn.create_table :full_drop_test_temp, :temporary => true do |t|
|
191
|
+
t.string :foo
|
192
|
+
end
|
193
|
+
#view
|
194
|
+
@conn.execute <<-SQL
|
195
|
+
create or replace view full_drop_test_view (foo) as select id as "foo" from full_drop_test
|
196
|
+
SQL
|
197
|
+
#package
|
198
|
+
@conn.execute <<-SQL
|
199
|
+
create or replace package full_drop_test_package as
|
200
|
+
function test_func return varchar2;
|
201
|
+
end test_package;
|
202
|
+
SQL
|
203
|
+
@conn.execute <<-SQL
|
204
|
+
create or replace package body full_drop_test_package as
|
205
|
+
function test_func return varchar2 is
|
206
|
+
begin
|
207
|
+
return ('foo');
|
208
|
+
end test_func;
|
209
|
+
end test_package;
|
210
|
+
SQL
|
211
|
+
#function
|
212
|
+
@conn.execute <<-SQL
|
213
|
+
create or replace function full_drop_test_function
|
214
|
+
return varchar2
|
215
|
+
is
|
216
|
+
foo varchar2(3);
|
217
|
+
begin
|
218
|
+
return('foo');
|
219
|
+
end;
|
220
|
+
SQL
|
221
|
+
#procedure
|
222
|
+
@conn.execute <<-SQL
|
223
|
+
create or replace procedure full_drop_test_procedure
|
224
|
+
begin
|
225
|
+
delete from full_drop_test where id=1231231231
|
226
|
+
exception
|
227
|
+
when no_data_found then
|
228
|
+
dbms_output.put_line('foo');
|
229
|
+
end;
|
230
|
+
SQL
|
231
|
+
#synonym
|
232
|
+
@conn.execute <<-SQL
|
233
|
+
create or replace synonym full_drop_test_synonym for full_drop_test
|
234
|
+
SQL
|
235
|
+
#type
|
236
|
+
@conn.execute <<-SQL
|
237
|
+
create or replace type full_drop_test_type as table of number
|
238
|
+
SQL
|
239
|
+
end
|
240
|
+
after(:each) do
|
241
|
+
@conn.drop_table :full_drop_test
|
242
|
+
@conn.drop_table :full_drop_test_temp
|
243
|
+
@conn.execute "DROP VIEW FULL_DROP_TEST_VIEW" rescue nil
|
244
|
+
@conn.execute "DROP SYNONYM FULL_DROP_TEST_SYNONYM" rescue nil
|
245
|
+
@conn.execute "DROP PACKAGE FULL_DROP_TEST_PACKAGE" rescue nil
|
246
|
+
@conn.execute "DROP FUNCTION FULL_DROP_TEST_FUNCTION" rescue nil
|
247
|
+
@conn.execute "DROP PROCEDURE FULL_DROP_TEST_PROCEDURE" rescue nil
|
248
|
+
@conn.execute "DROP TYPE FULL_DROP_TEST_TYPE" rescue nil
|
249
|
+
end
|
250
|
+
it "should contain correct sql" do
|
251
|
+
drop = @conn.full_drop
|
252
|
+
drop.should =~ /drop table full_drop_test cascade constraints/i
|
253
|
+
drop.should =~ /drop sequence full_drop_test_seq/i
|
254
|
+
drop.should =~ /drop view "full_drop_test_view"/i
|
255
|
+
drop.should =~ /drop package full_drop_test_package/i
|
256
|
+
drop.should =~ /drop function full_drop_test_function/i
|
257
|
+
drop.should =~ /drop procedure full_drop_test_procedure/i
|
258
|
+
drop.should =~ /drop synonym "full_drop_test_synonym"/i
|
259
|
+
drop.should =~ /drop type "full_drop_test_type"/i
|
260
|
+
end
|
261
|
+
it "should not drop tables when preserve_tables is true" do
|
262
|
+
drop = @conn.full_drop(true)
|
263
|
+
drop.should =~ /drop table full_drop_test_temp/i
|
264
|
+
drop.should_not =~ /drop table full_drop_test cascade constraints/i
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|