activerecord-oracle_enhanced-adapter 1.2.3 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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