activerecord-oracle_enhanced-adapter 1.2.4 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/History.txt +20 -0
- data/README.rdoc +7 -3
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/activerecord-oracle_enhanced-adapter.gemspec +96 -0
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +11 -8
- data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +37 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +317 -180
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +282 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +6 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +143 -52
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +39 -20
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +70 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +27 -20
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +334 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +28 -22
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +24 -28
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +13 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +72 -69
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +112 -6
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +49 -1
- data/spec/spec_helper.rb +97 -19
- metadata +33 -22
- data/Manifest.txt +0 -32
- data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +0 -126
@@ -6,7 +6,8 @@ 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, :
|
9
|
+
class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :type, :parameters, :statement_parameters,
|
10
|
+
:tablespace, :columns) #:nodoc:
|
10
11
|
end
|
11
12
|
|
12
13
|
module OracleEnhancedSchemaDefinitions #:nodoc:
|
@@ -11,26 +11,30 @@ module ActiveRecord #:nodoc:
|
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
14
|
-
|
14
|
+
|
15
|
+
def ignore_table?(table)
|
16
|
+
[ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
|
17
|
+
case ignored
|
18
|
+
when String; table == ignored
|
19
|
+
when Regexp; table =~ ignored
|
20
|
+
else
|
21
|
+
raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
15
26
|
def tables_with_oracle_enhanced(stream)
|
16
|
-
|
27
|
+
return tables_without_oracle_enhanced(stream) unless @connection.respond_to?(:materialized_views)
|
28
|
+
# do not include materialized views in schema dump - they should be created separately after schema creation
|
29
|
+
sorted_tables = (@connection.tables - @connection.materialized_views).sort
|
17
30
|
sorted_tables.each do |tbl|
|
18
31
|
# add table prefix or suffix for schema_migrations
|
19
|
-
next if
|
20
|
-
case ignored
|
21
|
-
when String; tbl == ignored
|
22
|
-
when Regexp; tbl =~ ignored
|
23
|
-
else
|
24
|
-
raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
|
25
|
-
end
|
26
|
-
end
|
32
|
+
next if ignore_table? tbl
|
27
33
|
# change table name inspect method
|
28
34
|
tbl.extend TableInspect
|
29
35
|
oracle_enhanced_table(tbl, stream)
|
30
36
|
# add primary key trigger if table has it
|
31
37
|
primary_key_trigger(tbl, stream)
|
32
|
-
end
|
33
|
-
sorted_tables.each do |tbl|
|
34
38
|
# add foreign keys if table has them
|
35
39
|
foreign_keys(tbl, stream)
|
36
40
|
end
|
@@ -76,6 +80,7 @@ module ActiveRecord #:nodoc:
|
|
76
80
|
if @connection.respond_to?(:synonyms)
|
77
81
|
syns = @connection.synonyms
|
78
82
|
syns.each do |syn|
|
83
|
+
next if ignore_table? syn.name
|
79
84
|
table_name = syn.table_name
|
80
85
|
table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
|
81
86
|
table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
|
@@ -89,14 +94,28 @@ module ActiveRecord #:nodoc:
|
|
89
94
|
def indexes_with_oracle_enhanced(table, stream)
|
90
95
|
if (indexes = @connection.indexes(table)).any?
|
91
96
|
add_index_statements = indexes.map do |index|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
case index.type
|
98
|
+
when nil
|
99
|
+
# use table.inspect as it will remove prefix and suffix
|
100
|
+
statement_parts = [ ('add_index ' + table.inspect) ]
|
101
|
+
statement_parts << index.columns.inspect
|
102
|
+
statement_parts << (':name => ' + index.name.inspect)
|
103
|
+
statement_parts << ':unique => true' if index.unique
|
104
|
+
statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
|
105
|
+
when 'CTXSYS.CONTEXT'
|
106
|
+
if index.statement_parameters
|
107
|
+
statement_parts = [ ('add_context_index ' + table.inspect) ]
|
108
|
+
statement_parts << index.statement_parameters
|
109
|
+
else
|
110
|
+
statement_parts = [ ('add_context_index ' + table.inspect) ]
|
111
|
+
statement_parts << index.columns.inspect
|
112
|
+
statement_parts << (':name => ' + index.name.inspect)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
# unrecognized index type
|
116
|
+
statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"]
|
117
|
+
end
|
118
|
+
' ' + statement_parts.join(', ')
|
100
119
|
end
|
101
120
|
|
102
121
|
stream.puts add_index_statements.sort.join("\n")
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# implementation idea taken from JDBC adapter
|
2
|
-
if defined?(Rake.application) && Rake.application &&
|
2
|
+
if defined?(Rake.application) && Rake.application &&
|
3
|
+
ActiveRecord::Base.configurations[defined?(Rails.env) ? Rails.env : RAILS_ENV]['adapter'] == 'oracle_enhanced'
|
3
4
|
oracle_enhanced_rakefile = File.dirname(__FILE__) + "/oracle_enhanced.rake"
|
4
5
|
if Rake.application.lookup("environment")
|
5
6
|
# rails tasks already defined; load the override tasks now
|
@@ -1 +1 @@
|
|
1
|
-
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION =
|
1
|
+
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = File.read(File.dirname(__FILE__)+'/../../../VERSION').chomp
|
@@ -112,7 +112,7 @@ describe "OracleEnhancedAdapter" do
|
|
112
112
|
before(:all) do
|
113
113
|
@conn.execute <<-SQL
|
114
114
|
CREATE TABLE test_employees (
|
115
|
-
id NUMBER,
|
115
|
+
id NUMBER PRIMARY KEY,
|
116
116
|
first_name VARCHAR2(20),
|
117
117
|
last_name VARCHAR2(25),
|
118
118
|
email VARCHAR2(25),
|
@@ -180,7 +180,7 @@ describe "OracleEnhancedAdapter" do
|
|
180
180
|
@conn.execute "DROP TABLE test_employees" rescue nil
|
181
181
|
@conn.execute <<-SQL
|
182
182
|
CREATE TABLE test_employees (
|
183
|
-
id NUMBER,
|
183
|
+
id NUMBER PRIMARY KEY,
|
184
184
|
first_name VARCHAR2(20),
|
185
185
|
last_name VARCHAR2(25),
|
186
186
|
hire_date DATE
|
@@ -198,12 +198,15 @@ describe "OracleEnhancedAdapter" do
|
|
198
198
|
end
|
199
199
|
|
200
200
|
before(:each) do
|
201
|
-
|
202
|
-
log_to @buffer
|
201
|
+
set_logger
|
203
202
|
@conn = ActiveRecord::Base.connection
|
204
203
|
@conn.clear_columns_cache
|
205
204
|
end
|
206
205
|
|
206
|
+
after(:each) do
|
207
|
+
clear_logger
|
208
|
+
end
|
209
|
+
|
207
210
|
describe "without column caching" do
|
208
211
|
|
209
212
|
before(:each) do
|
@@ -212,14 +215,26 @@ describe "OracleEnhancedAdapter" do
|
|
212
215
|
|
213
216
|
it "should get columns from database at first time" do
|
214
217
|
TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
|
215
|
-
@
|
218
|
+
@logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
|
216
219
|
end
|
217
220
|
|
218
221
|
it "should get columns from database at second time" do
|
219
222
|
TestEmployee.connection.columns('test_employees')
|
220
|
-
@
|
223
|
+
@logger.clear(:debug)
|
221
224
|
TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
|
222
|
-
@
|
225
|
+
@logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should get primary key from database at first time" do
|
229
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
230
|
+
@logger.logged(:debug).last.should =~ /select .* from all_constraints/im
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should get primary key from database at first time" do
|
234
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
235
|
+
@logger.clear(:debug)
|
236
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
237
|
+
@logger.logged(:debug).last.should =~ /select .* from all_constraints/im
|
223
238
|
end
|
224
239
|
|
225
240
|
end
|
@@ -232,14 +247,26 @@ describe "OracleEnhancedAdapter" do
|
|
232
247
|
|
233
248
|
it "should get columns from database at first time" do
|
234
249
|
TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
|
235
|
-
@
|
250
|
+
@logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
|
236
251
|
end
|
237
252
|
|
238
253
|
it "should get columns from cache at second time" do
|
239
254
|
TestEmployee.connection.columns('test_employees')
|
240
|
-
@
|
255
|
+
@logger.clear(:debug)
|
241
256
|
TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
|
242
|
-
@
|
257
|
+
@logger.logged(:debug).last.should be_blank
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should get primary key from database at first time" do
|
261
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
262
|
+
@logger.logged(:debug).last.should =~ /select .* from all_constraints/im
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should get primary key from cache at first time" do
|
266
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
267
|
+
@logger.clear(:debug)
|
268
|
+
TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
|
269
|
+
@logger.logged(:debug).last.should be_blank
|
243
270
|
end
|
244
271
|
|
245
272
|
end
|
@@ -329,6 +356,14 @@ describe "OracleEnhancedAdapter" do
|
|
329
356
|
@adapter.valid_table_name?("sys.v$session").should be_true
|
330
357
|
end
|
331
358
|
|
359
|
+
it "should be valid with upcase schema name" do
|
360
|
+
@adapter.valid_table_name?("ABC_123.DEF_456").should be_true
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should be valid with irregular schema name and database links" do
|
364
|
+
@adapter.valid_table_name?('abc$#_123.abc$#_123@abc$#@._123').should be_true
|
365
|
+
end
|
366
|
+
|
332
367
|
it "should not be valid with two dots in name" do
|
333
368
|
@adapter.valid_table_name?("abc_123.def_456.ghi_789").should be_false
|
334
369
|
end
|
@@ -337,6 +372,30 @@ describe "OracleEnhancedAdapter" do
|
|
337
372
|
@adapter.valid_table_name?("warehouse-things").should be_false
|
338
373
|
end
|
339
374
|
|
375
|
+
it "should not be valid with for camel-case" do
|
376
|
+
@adapter.valid_table_name?("Abc").should be_false
|
377
|
+
@adapter.valid_table_name?("aBc").should be_false
|
378
|
+
@adapter.valid_table_name?("abC").should be_false
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should not be valid for names > 30 characters" do
|
382
|
+
@adapter.valid_table_name?("a" * 31).should be_false
|
383
|
+
end
|
384
|
+
|
385
|
+
it "should not be valid for schema names > 30 characters" do
|
386
|
+
@adapter.valid_table_name?(("a" * 31) + ".validname").should be_false
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should not be valid for database links > 128 characters" do
|
390
|
+
@adapter.valid_table_name?("name@" + "a" * 129).should be_false
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should not be valid for names that do not begin with alphabetic characters" do
|
394
|
+
@adapter.valid_table_name?("1abc").should be_false
|
395
|
+
@adapter.valid_table_name?("_abc").should be_false
|
396
|
+
@adapter.valid_table_name?("abc.1xyz").should be_false
|
397
|
+
@adapter.valid_table_name?("abc._xyz").should be_false
|
398
|
+
end
|
340
399
|
end
|
341
400
|
|
342
401
|
describe "table quoting" do
|
@@ -415,7 +474,7 @@ describe "OracleEnhancedAdapter" do
|
|
415
474
|
@db_link_password = SYSTEM_CONNECTION_PARAMS[:password]
|
416
475
|
@db_link_database = SYSTEM_CONNECTION_PARAMS[:database]
|
417
476
|
@conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
|
418
|
-
@conn.execute "CREATE DATABASE LINK #{@db_link} CONNECT TO #{@db_link_username} IDENTIFIED BY #{@db_link_password} USING '#{@db_link_database}'"
|
477
|
+
@conn.execute "CREATE DATABASE LINK #{@db_link} CONNECT TO #{@db_link_username} IDENTIFIED BY \"#{@db_link_password}\" USING '#{@db_link_database}'"
|
419
478
|
@conn.execute "CREATE OR REPLACE SYNONYM test_posts FOR test_posts@#{@db_link}"
|
420
479
|
@conn.execute "CREATE OR REPLACE SYNONYM test_posts_seq FOR test_posts_seq@#{@db_link}"
|
421
480
|
class ::TestPost < ActiveRecord::Base
|
data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb
CHANGED
@@ -58,7 +58,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
58
58
|
SQL
|
59
59
|
dump = ActiveRecord::Base.connection.structure_dump_fk_constraints
|
60
60
|
dump.split('\n').length.should == 1
|
61
|
-
dump.should =~ /ALTER TABLE TEST_POSTS ADD CONSTRAINT
|
61
|
+
dump.should =~ /ALTER TABLE \"?TEST_POSTS\"? ADD CONSTRAINT \"?FK_TEST_POST_FOO\"? FOREIGN KEY \(\"?FOO_ID\"?\) REFERENCES \"?FOOS\"?\(\"?ID\"?\)/i
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should not error when no foreign keys are present" do
|
@@ -78,7 +78,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
78
78
|
END;
|
79
79
|
SQL
|
80
80
|
dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
|
81
|
-
dump.should =~ /
|
81
|
+
dump.should =~ /CREATE OR REPLACE TRIGGER TEST_POST_TRIGGER/
|
82
82
|
end
|
83
83
|
|
84
84
|
it "should dump types" do
|
@@ -86,7 +86,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
86
86
|
create or replace TYPE TEST_TYPE AS TABLE OF VARCHAR2(10);
|
87
87
|
SQL
|
88
88
|
dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
|
89
|
-
dump.should =~ /
|
89
|
+
dump.should =~ /CREATE OR REPLACE TYPE TEST_TYPE/
|
90
90
|
end
|
91
91
|
|
92
92
|
it "should dump virtual columns" do
|
@@ -99,7 +99,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
99
99
|
)
|
100
100
|
SQL
|
101
101
|
dump = ActiveRecord::Base.connection.structure_dump
|
102
|
-
dump.should =~ /
|
102
|
+
dump.should =~ /ID_PLUS NUMBER GENERATED ALWAYS AS \(ID\+2\) VIRTUAL/
|
103
103
|
end
|
104
104
|
|
105
105
|
it "should dump unique keys" do
|
@@ -124,9 +124,9 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
124
124
|
SQL
|
125
125
|
|
126
126
|
dump = ActiveRecord::Base.connection.structure_dump
|
127
|
-
dump.should =~ /
|
128
|
-
dump.should =~ /
|
129
|
-
dump.should_not =~ /
|
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
130
|
end
|
131
131
|
end
|
132
132
|
describe "temporary tables" do
|
@@ -138,7 +138,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
138
138
|
t.integer :post_id
|
139
139
|
end
|
140
140
|
dump = ActiveRecord::Base.connection.structure_dump
|
141
|
-
dump.should =~ /
|
141
|
+
dump.should =~ /CREATE GLOBAL TEMPORARY TABLE "?TEST_COMMENTS"?/i
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
@@ -173,8 +173,8 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
173
173
|
end
|
174
174
|
it "should dump drop sql for just temp tables" do
|
175
175
|
dump = @conn.temp_table_drop
|
176
|
-
dump.should =~ /
|
177
|
-
dump.should_not =~ /
|
176
|
+
dump.should =~ /DROP TABLE "TEMP_TBL"/
|
177
|
+
dump.should_not =~ /DROP TABLE "?NOT_TEMP_TBL"?/i
|
178
178
|
end
|
179
179
|
after(:each) do
|
180
180
|
@conn.drop_table :temp_tbl
|
@@ -194,6 +194,10 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
194
194
|
@conn.execute <<-SQL
|
195
195
|
create or replace view full_drop_test_view (foo) as select id as "foo" from full_drop_test
|
196
196
|
SQL
|
197
|
+
#materialized view
|
198
|
+
@conn.execute <<-SQL
|
199
|
+
create materialized view full_drop_test_mview (foo) as select id as "foo" from full_drop_test
|
200
|
+
SQL
|
197
201
|
#package
|
198
202
|
@conn.execute <<-SQL
|
199
203
|
create or replace package full_drop_test_package as
|
@@ -241,6 +245,7 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
241
245
|
@conn.drop_table :full_drop_test
|
242
246
|
@conn.drop_table :full_drop_test_temp
|
243
247
|
@conn.execute "DROP VIEW FULL_DROP_TEST_VIEW" rescue nil
|
248
|
+
@conn.execute "DROP MATERIALIZED VIEW FULL_DROP_TEST_MVIEW" rescue nil
|
244
249
|
@conn.execute "DROP SYNONYM FULL_DROP_TEST_SYNONYM" rescue nil
|
245
250
|
@conn.execute "DROP PACKAGE FULL_DROP_TEST_PACKAGE" rescue nil
|
246
251
|
@conn.execute "DROP FUNCTION FULL_DROP_TEST_FUNCTION" rescue nil
|
@@ -249,19 +254,21 @@ describe "OracleEnhancedAdapter structure dump" do
|
|
249
254
|
end
|
250
255
|
it "should contain correct sql" do
|
251
256
|
drop = @conn.full_drop
|
252
|
-
drop.should =~ /
|
253
|
-
drop.should =~ /
|
254
|
-
drop.should =~ /
|
255
|
-
drop.
|
256
|
-
drop.should =~ /
|
257
|
-
drop.should =~ /
|
258
|
-
drop.should =~ /
|
259
|
-
drop.should =~ /
|
257
|
+
drop.should =~ /DROP TABLE "FULL_DROP_TEST" CASCADE CONSTRAINTS/
|
258
|
+
drop.should =~ /DROP SEQUENCE "FULL_DROP_TEST_SEQ"/
|
259
|
+
drop.should =~ /DROP VIEW "FULL_DROP_TEST_VIEW"/
|
260
|
+
drop.should_not =~ /DROP TABLE "?FULL_DROP_TEST_MVIEW"?/i
|
261
|
+
drop.should =~ /DROP MATERIALIZED VIEW "FULL_DROP_TEST_MVIEW"/
|
262
|
+
drop.should =~ /DROP PACKAGE "FULL_DROP_TEST_PACKAGE"/
|
263
|
+
drop.should =~ /DROP FUNCTION "FULL_DROP_TEST_FUNCTION"/
|
264
|
+
drop.should =~ /DROP PROCEDURE "FULL_DROP_TEST_PROCEDURE"/
|
265
|
+
drop.should =~ /DROP SYNONYM "FULL_DROP_TEST_SYNONYM"/
|
266
|
+
drop.should =~ /DROP TYPE "FULL_DROP_TEST_TYPE"/
|
260
267
|
end
|
261
268
|
it "should not drop tables when preserve_tables is true" do
|
262
269
|
drop = @conn.full_drop(true)
|
263
|
-
drop.should =~ /
|
264
|
-
drop.should_not =~ /
|
270
|
+
drop.should =~ /DROP TABLE "FULL_DROP_TEST_TEMP"/
|
271
|
+
drop.should_not =~ /DROP TABLE "?FULL_DROP_TEST"? CASCADE CONSTRAINTS/i
|
265
272
|
end
|
266
273
|
end
|
267
274
|
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe "OracleEnhancedAdapter context index" do
|
4
|
+
include SchemaSpecHelper
|
5
|
+
include LoggerSpecHelper
|
6
|
+
|
7
|
+
def create_table_posts
|
8
|
+
schema_define do
|
9
|
+
create_table :posts, :force => true do |t|
|
10
|
+
t.string :title
|
11
|
+
t.text :body
|
12
|
+
t.integer :comments_count
|
13
|
+
t.timestamps
|
14
|
+
t.string :all_text, :limit => 2 # will be used for multi-column index
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_table_comments
|
20
|
+
schema_define do
|
21
|
+
create_table :comments, :force => true do |t|
|
22
|
+
t.integer :post_id
|
23
|
+
t.string :author
|
24
|
+
t.text :body
|
25
|
+
t.timestamps
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_tables
|
31
|
+
create_table_posts
|
32
|
+
create_table_comments
|
33
|
+
end
|
34
|
+
|
35
|
+
def drop_table_posts
|
36
|
+
schema_define { drop_table :posts }
|
37
|
+
end
|
38
|
+
|
39
|
+
def drop_table_comments
|
40
|
+
schema_define { drop_table :comments }
|
41
|
+
end
|
42
|
+
|
43
|
+
def drop_tables
|
44
|
+
drop_table_comments
|
45
|
+
drop_table_posts
|
46
|
+
end
|
47
|
+
|
48
|
+
before(:all) do
|
49
|
+
# database user should have CTXAPP role to be able to set CONTEXT index parameters
|
50
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
51
|
+
@conn = ActiveRecord::Base.connection
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "on single table" do
|
55
|
+
before(:all) do
|
56
|
+
@title_words = %w{aaa bbb ccc}
|
57
|
+
@body_words = %w{foo bar baz}
|
58
|
+
create_table_posts
|
59
|
+
class ::Post < ActiveRecord::Base
|
60
|
+
has_context_index
|
61
|
+
end
|
62
|
+
@post0 = Post.create(:title => "dummy title", :body => "dummy body")
|
63
|
+
@post1 = Post.create(:title => @title_words.join(' '), :body => @body_words.join(' '))
|
64
|
+
@post2 = Post.create(:title => (@title_words*2).join(' '), :body => (@body_words*2).join(' '))
|
65
|
+
end
|
66
|
+
|
67
|
+
after(:all) do
|
68
|
+
drop_table_posts
|
69
|
+
Object.send(:remove_const, "Post")
|
70
|
+
end
|
71
|
+
|
72
|
+
after(:each) do
|
73
|
+
@post.destroy if @post
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should create single VARCHAR2 column index" do
|
77
|
+
@conn.add_context_index :posts, :title
|
78
|
+
@title_words.each do |word|
|
79
|
+
Post.contains(:title, word).all.should == [@post2, @post1]
|
80
|
+
end
|
81
|
+
@conn.remove_context_index :posts, :title
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should create single CLOB column index" do
|
85
|
+
@conn.add_context_index :posts, :body
|
86
|
+
@body_words.each do |word|
|
87
|
+
Post.contains(:body, word).all.should == [@post2, @post1]
|
88
|
+
end
|
89
|
+
@conn.remove_context_index :posts, :body
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should not include text index secondary tables in user tables list" do
|
93
|
+
@conn.add_context_index :posts, :title
|
94
|
+
@conn.tables.any?{|t| t =~ /^dr\$/i}.should be_false
|
95
|
+
@conn.remove_context_index :posts, :title
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should create multiple column index" do
|
99
|
+
@conn.add_context_index :posts, [:title, :body]
|
100
|
+
(@title_words+@body_words).each do |word|
|
101
|
+
Post.contains(:title, word).all.should == [@post2, @post1]
|
102
|
+
end
|
103
|
+
@conn.remove_context_index :posts, [:title, :body]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should create multiple column index with specified main index column" do
|
107
|
+
@conn.add_context_index :posts, [:title, :body],
|
108
|
+
:index_column => :all_text, :sync => 'ON COMMIT'
|
109
|
+
@post = Post.create(:title => "abc", :body => "def")
|
110
|
+
Post.contains(:all_text, "abc").all.should == [@post]
|
111
|
+
Post.contains(:all_text, "def").all.should == [@post]
|
112
|
+
@post.update_attributes!(:title => "ghi")
|
113
|
+
# index will not be updated as all_text column is not changed
|
114
|
+
Post.contains(:all_text, "ghi").all.should be_empty
|
115
|
+
@post.update_attributes!(:all_text => "1")
|
116
|
+
# index will be updated when all_text column is changed
|
117
|
+
Post.contains(:all_text, "ghi").all.should == [@post]
|
118
|
+
@conn.remove_context_index :posts, :index_column => :all_text
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should create multiple column index with trigger updated main index column" do
|
122
|
+
@conn.add_context_index :posts, [:title, :body],
|
123
|
+
:index_column => :all_text, :index_column_trigger_on => [:created_at, :updated_at],
|
124
|
+
:sync => 'ON COMMIT'
|
125
|
+
@post = Post.create(:title => "abc", :body => "def")
|
126
|
+
Post.contains(:all_text, "abc").all.should == [@post]
|
127
|
+
Post.contains(:all_text, "def").all.should == [@post]
|
128
|
+
@post.update_attributes!(:title => "ghi")
|
129
|
+
# index should be updated as created_at column is changed
|
130
|
+
Post.contains(:all_text, "ghi").all.should == [@post]
|
131
|
+
@conn.remove_context_index :posts, :index_column => :all_text
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "on multiple tables" do
|
137
|
+
before(:all) do
|
138
|
+
create_tables
|
139
|
+
class ::Post < ActiveRecord::Base
|
140
|
+
has_many :comments, :dependent => :destroy
|
141
|
+
has_context_index
|
142
|
+
end
|
143
|
+
class ::Comment < ActiveRecord::Base
|
144
|
+
belongs_to :post, :counter_cache => true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
after(:all) do
|
149
|
+
drop_tables
|
150
|
+
Object.send(:remove_const, "Comment")
|
151
|
+
Object.send(:remove_const, "Post")
|
152
|
+
end
|
153
|
+
|
154
|
+
after(:each) do
|
155
|
+
Post.destroy_all
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should create multiple table index with specified main index column" do
|
159
|
+
@conn.add_context_index :posts,
|
160
|
+
[:title, :body,
|
161
|
+
# specify aliases always with AS keyword
|
162
|
+
"SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"
|
163
|
+
],
|
164
|
+
:name => 'post_and_comments_index',
|
165
|
+
:index_column => :all_text, :index_column_trigger_on => [:updated_at, :comments_count],
|
166
|
+
:sync => 'ON COMMIT'
|
167
|
+
@post = Post.create!(:title => "aaa", :body => "bbb")
|
168
|
+
@post.comments.create!(:author => "ccc", :body => "ddd")
|
169
|
+
@post.comments.create!(:author => "eee", :body => "fff")
|
170
|
+
["aaa", "bbb", "ccc", "ddd", "eee", "fff"].each do |word|
|
171
|
+
Post.contains(:all_text, word).all.should == [@post]
|
172
|
+
end
|
173
|
+
@conn.remove_context_index :posts, :name => 'post_and_comments_index'
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should find by search term within specified field" do
|
177
|
+
@post = Post.create!(:title => "aaa", :body => "bbb")
|
178
|
+
@post.comments.create!(:author => "ccc", :body => "ddd")
|
179
|
+
@conn.add_context_index :posts,
|
180
|
+
[:title, :body,
|
181
|
+
# specify aliases always with AS keyword
|
182
|
+
"SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"
|
183
|
+
],
|
184
|
+
:index_column => :all_text
|
185
|
+
Post.contains(:all_text, "aaa within title").all.should == [@post]
|
186
|
+
Post.contains(:all_text, "aaa within body").all.should be_empty
|
187
|
+
Post.contains(:all_text, "bbb within body").all.should == [@post]
|
188
|
+
Post.contains(:all_text, "bbb within title").all.should be_empty
|
189
|
+
Post.contains(:all_text, "ccc within comment_author").all.should == [@post]
|
190
|
+
Post.contains(:all_text, "ccc within comment_body").all.should be_empty
|
191
|
+
Post.contains(:all_text, "ddd within comment_body").all.should == [@post]
|
192
|
+
Post.contains(:all_text, "ddd within comment_author").all.should be_empty
|
193
|
+
@conn.remove_context_index :posts, :index_column => :all_text
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "with specified tablespace" do
|
199
|
+
before(:all) do
|
200
|
+
create_table_posts
|
201
|
+
class ::Post < ActiveRecord::Base
|
202
|
+
has_context_index
|
203
|
+
end
|
204
|
+
@post = Post.create(:title => 'aaa', :body => 'bbb')
|
205
|
+
@tablespace = @conn.default_tablespace
|
206
|
+
set_logger
|
207
|
+
@conn = ActiveRecord::Base.connection
|
208
|
+
end
|
209
|
+
|
210
|
+
after(:all) do
|
211
|
+
drop_table_posts
|
212
|
+
Object.send(:remove_const, "Post")
|
213
|
+
end
|
214
|
+
|
215
|
+
after(:each) do
|
216
|
+
clear_logger
|
217
|
+
end
|
218
|
+
|
219
|
+
def verify_logged_statements
|
220
|
+
['K_TABLE_CLAUSE', 'R_TABLE_CLAUSE', 'N_TABLE_CLAUSE', 'I_INDEX_CLAUSE', 'P_TABLE_CLAUSE'].each do |clause|
|
221
|
+
@logger.output(:debug).should =~ /CTX_DDL\.SET_ATTRIBUTE\('index_posts_on_title_sto', '#{clause}', '.*TABLESPACE #{@tablespace}'\)/
|
222
|
+
end
|
223
|
+
@logger.output(:debug).should =~ /CREATE INDEX .* PARAMETERS \('STORAGE index_posts_on_title_sto'\)/
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should create index on single column" do
|
227
|
+
@conn.add_context_index :posts, :title, :tablespace => @tablespace
|
228
|
+
verify_logged_statements
|
229
|
+
Post.contains(:title, 'aaa').all.should == [@post]
|
230
|
+
@conn.remove_context_index :posts, :title
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should create index on multiple columns" do
|
234
|
+
@conn.add_context_index :posts, [:title, :body], :name => 'index_posts_text', :tablespace => @conn.default_tablespace
|
235
|
+
verify_logged_statements
|
236
|
+
Post.contains(:title, 'aaa AND bbb').all.should == [@post]
|
237
|
+
@conn.remove_context_index :posts, :name => 'index_posts_text'
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "schema dump" do
|
243
|
+
|
244
|
+
def standard_dump
|
245
|
+
stream = StringIO.new
|
246
|
+
ActiveRecord::SchemaDumper.ignore_tables = []
|
247
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
248
|
+
stream.string
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "without table prefixe and suffix" do
|
252
|
+
|
253
|
+
before(:all) do
|
254
|
+
create_tables
|
255
|
+
end
|
256
|
+
|
257
|
+
after(:all) do
|
258
|
+
drop_tables
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should dump definition of single column index" do
|
262
|
+
@conn.add_context_index :posts, :title
|
263
|
+
standard_dump.should =~ /add_context_index "posts", \["title"\], :name => \"index_posts_on_title\"$/
|
264
|
+
@conn.remove_context_index :posts, :title
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should dump definition of multiple column index" do
|
268
|
+
@conn.add_context_index :posts, [:title, :body]
|
269
|
+
standard_dump.should =~ /add_context_index "posts", \[:title, :body\]$/
|
270
|
+
@conn.remove_context_index :posts, [:title, :body]
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should dump definition of multiple table index with options" do
|
274
|
+
options = {
|
275
|
+
:name => 'post_and_comments_index',
|
276
|
+
:index_column => :all_text, :index_column_trigger_on => :updated_at,
|
277
|
+
:sync => 'ON COMMIT'
|
278
|
+
}
|
279
|
+
@conn.add_context_index :posts,
|
280
|
+
[:title, :body,
|
281
|
+
"SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"
|
282
|
+
], options
|
283
|
+
standard_dump.should =~ /add_context_index "posts", \[:title, :body, "SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"\], #{options.inspect[1..-2]}$/
|
284
|
+
@conn.remove_context_index :posts, :name => 'post_and_comments_index'
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "with table prefix and suffix" do
|
290
|
+
before(:all) do
|
291
|
+
ActiveRecord::Base.table_name_prefix = 'xxx_'
|
292
|
+
ActiveRecord::Base.table_name_suffix = '_xxx'
|
293
|
+
create_tables
|
294
|
+
end
|
295
|
+
|
296
|
+
after(:all) do
|
297
|
+
drop_tables
|
298
|
+
ActiveRecord::Base.table_name_prefix = ''
|
299
|
+
ActiveRecord::Base.table_name_suffix = ''
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should dump definition of single column index" do
|
303
|
+
schema_define { add_context_index :posts, :title }
|
304
|
+
standard_dump.should =~ /add_context_index "posts", \["title"\], :name => "i_xxx_posts_xxx_title"$/
|
305
|
+
schema_define { remove_context_index :posts, :title }
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should dump definition of multiple column index" do
|
309
|
+
schema_define { add_context_index :posts, [:title, :body] }
|
310
|
+
standard_dump.should =~ /add_context_index "posts", \[:title, :body\]$/
|
311
|
+
schema_define { remove_context_index :posts, [:title, :body] }
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should dump definition of multiple table index with options" do
|
315
|
+
options = {
|
316
|
+
:name => 'xxx_post_and_comments_i',
|
317
|
+
:index_column => :all_text, :index_column_trigger_on => :updated_at,
|
318
|
+
:sync => 'ON COMMIT'
|
319
|
+
}
|
320
|
+
schema_define do
|
321
|
+
add_context_index :posts,
|
322
|
+
[:title, :body,
|
323
|
+
"SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"
|
324
|
+
], options
|
325
|
+
end
|
326
|
+
standard_dump.should =~ /add_context_index "posts", \[:title, :body, "SELECT comments.author AS comment_author, comments.body AS comment_body FROM comments WHERE comments.post_id = :id"\], #{options.inspect[1..-2]}$/
|
327
|
+
schema_define { remove_context_index :posts, :name => 'xxx_post_and_comments_i' }
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|