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.
@@ -23,14 +23,7 @@ begin
23
23
 
24
24
  rescue LoadError, NameError
25
25
  # JDBC driver is unavailable.
26
- error_message = "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. "+
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, password, database = config[:username].to_s, config[:password].to_s, config[:database].to_s
58
- privilege = config[:privilege] && config[:privilege].to_s
59
- host, port = config[:host], config[:port]
60
-
61
- # connection using TNS alias
62
- if database && !host && !config[:url] && ENV['TNS_ADMIN']
63
- url = "jdbc:oracle:thin:@#{database || 'XE'}"
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
- url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}:#{database || 'XE'}"
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
- # get session time_zone from configuration or from TZ environment variable
73
- time_zone = config[:time_zone] || ENV['TZ'] || java.util.TimeZone.default.getID
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 'oci8' unless self.class.const_defined? :OCI8
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
- error_message = "ERROR: ActiveRecord oracle_enhanced adapter could not load ruby-oci8 library. "+
15
- "Please install ruby-oci8 library or gem."
16
- if defined?(RAILS_DEFAULT_LOGGER)
17
- RAILS_DEFAULT_LOGGER.error error_message
18
- else
19
- STDERR.puts error_message
20
- end
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
- table(tbl, stream)
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 ::Employee < ActiveRecord::Base
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 { Employee.count(:employee_id, :distinct => true) }.should_not raise_error
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