activerecord-oracle_enhanced-adapter-with-schema 0.0.1

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.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +52 -0
  3. data/History.md +301 -0
  4. data/License.txt +20 -0
  5. data/README.md +123 -0
  6. data/RUNNING_TESTS.md +45 -0
  7. data/Rakefile +59 -0
  8. data/VERSION +1 -0
  9. data/activerecord-oracle_enhanced-adapter-with-schema.gemspec +130 -0
  10. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1399 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +121 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +146 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +119 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +46 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +494 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +227 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +260 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +428 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +258 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
  29. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
  30. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  31. data/lib/activerecord-oracle_enhanced-adapter-with-schema.rb +25 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +778 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +332 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +427 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +19 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +113 -0
  37. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +1388 -0
  38. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +69 -0
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +141 -0
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +378 -0
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +440 -0
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1385 -0
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
  45. data/spec/spec_helper.rb +189 -0
  46. metadata +260 -0
@@ -0,0 +1,17 @@
1
+ # Used just for Rails 2.x
2
+ # In Rails 3.x rake tasks are loaded using railtie
3
+ if ActiveRecord::VERSION::MAJOR == 2
4
+
5
+ if defined?(Rake.application) && Rake.application &&
6
+ ActiveRecord::Base.configurations[defined?(Rails.env) ? Rails.env : RAILS_ENV]['adapter'] == 'oracle_enhanced'
7
+ oracle_enhanced_rakefile = File.dirname(__FILE__) + "/oracle_enhanced.rake"
8
+ if Rake.application.lookup("environment")
9
+ # rails tasks already defined; load the override tasks now
10
+ load oracle_enhanced_rakefile
11
+ else
12
+ # rails tasks not loaded yet; load as an import
13
+ Rake.application.add_import(oracle_enhanced_rakefile)
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1 @@
1
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = File.read(File.dirname(__FILE__)+'/../../../VERSION').chomp
@@ -0,0 +1,25 @@
1
+ # define railtie which will be executed in Rails 3
2
+ if defined?(::Rails::Railtie)
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class OracleEnhancedRailtie < ::Rails::Railtie
7
+ rake_tasks do
8
+ load 'active_record/connection_adapters/oracle_enhanced.rake'
9
+ end
10
+
11
+ ActiveSupport.on_load(:active_record) do
12
+ require 'active_record/connection_adapters/oracle_enhanced_adapter'
13
+
14
+ # Cache column descriptions between requests in test and production environments
15
+ if Rails.env.test? || Rails.env.production?
16
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,778 @@
1
+ require 'spec_helper'
2
+
3
+ describe "OracleEnhancedAdapter establish connection" do
4
+
5
+ it "should connect to database" do
6
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
7
+ ActiveRecord::Base.connection.should_not be_nil
8
+ ActiveRecord::Base.connection.class.should == ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
9
+ end
10
+
11
+ it "should connect to database as SYSDBA" do
12
+ ActiveRecord::Base.establish_connection(SYS_CONNECTION_PARAMS)
13
+ ActiveRecord::Base.connection.should_not be_nil
14
+ ActiveRecord::Base.connection.class.should == ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
15
+ end
16
+
17
+ it "should be active after connection to database" do
18
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
19
+ ActiveRecord::Base.connection.should be_active
20
+ end
21
+
22
+ it "should not be active after disconnection to database" do
23
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
24
+ ActiveRecord::Base.connection.disconnect!
25
+ ActiveRecord::Base.connection.should_not be_active
26
+ end
27
+
28
+ it "should be active after reconnection to database" do
29
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
30
+ ActiveRecord::Base.connection.reconnect!
31
+ ActiveRecord::Base.connection.should be_active
32
+ end
33
+
34
+ end
35
+
36
+ describe "OracleEnhancedAdapter" do
37
+ include LoggerSpecHelper
38
+ include SchemaSpecHelper
39
+
40
+ before(:all) do
41
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
42
+ end
43
+
44
+ describe "database session store" do
45
+ before(:all) do
46
+ @conn.execute "DROP TABLE sessions" rescue nil
47
+ @conn.execute "DROP SEQUENCE sessions_seq" rescue nil
48
+ @conn = ActiveRecord::Base.connection
49
+ @conn.execute <<-SQL
50
+ CREATE TABLE sessions (
51
+ id NUMBER(38,0) NOT NULL,
52
+ session_id VARCHAR2(255) DEFAULT NULL,
53
+ data CLOB DEFAULT NULL,
54
+ created_at DATE DEFAULT NULL,
55
+ updated_at DATE DEFAULT NULL,
56
+ PRIMARY KEY (ID)
57
+ )
58
+ SQL
59
+ @conn.execute <<-SQL
60
+ CREATE SEQUENCE sessions_seq MINVALUE 1 MAXVALUE 999999999999999999999999999
61
+ INCREMENT BY 1 START WITH 10040 CACHE 20 NOORDER NOCYCLE
62
+ SQL
63
+ if ENV['RAILS_GEM_VERSION'] >= '2.3'
64
+ @session_class = ActiveRecord::SessionStore::Session
65
+ else
66
+ @session_class = CGI::Session::ActiveRecordStore::Session
67
+ end
68
+ end
69
+
70
+ after(:all) do
71
+ @conn.execute "DROP TABLE sessions"
72
+ @conn.execute "DROP SEQUENCE sessions_seq"
73
+ end
74
+
75
+ it "should create sessions table" do
76
+ ActiveRecord::Base.connection.tables.grep("sessions").should_not be_empty
77
+ end
78
+
79
+ it "should save session data" do
80
+ @session = @session_class.new :session_id => "111111", :data => "something" #, :updated_at => Time.now
81
+ @session.save!
82
+ @session = @session_class.find_by_session_id("111111")
83
+ @session.data.should == "something"
84
+ end
85
+
86
+ it "should change session data when partial updates enabled" do
87
+ return pending("Not in this ActiveRecord version") unless @session_class.respond_to?(:partial_updates=)
88
+ @session_class.partial_updates = true
89
+ @session = @session_class.new :session_id => "222222", :data => "something" #, :updated_at => Time.now
90
+ @session.save!
91
+ @session = @session_class.find_by_session_id("222222")
92
+ @session.data = "other thing"
93
+ @session.save!
94
+ # second save should call again blob writing callback
95
+ @session.save!
96
+ @session = @session_class.find_by_session_id("222222")
97
+ @session.data.should == "other thing"
98
+ end
99
+
100
+ it "should have one enhanced_write_lobs callback" do
101
+ return pending("Not in this ActiveRecord version") unless @session_class.respond_to?(:after_save_callback_chain)
102
+ @session_class.after_save_callback_chain.select{|cb| cb.method == :enhanced_write_lobs}.should have(1).record
103
+ end
104
+
105
+ it "should not set sessions table session_id column type as integer if emulate_integers_by_column_name is true" do
106
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
107
+ columns = @conn.columns('sessions')
108
+ column = columns.detect{|c| c.name == "session_id"}
109
+ column.type.should == :string
110
+ end
111
+
112
+ end
113
+
114
+ describe "ignore specified table columns" do
115
+ before(:all) do
116
+ @conn = ActiveRecord::Base.connection
117
+ @conn.execute "DROP TABLE test_employees" rescue nil
118
+ @conn.execute <<-SQL
119
+ CREATE TABLE test_employees (
120
+ id NUMBER PRIMARY KEY,
121
+ first_name VARCHAR2(20),
122
+ last_name VARCHAR2(25),
123
+ email VARCHAR2(25),
124
+ phone_number VARCHAR2(20),
125
+ hire_date DATE,
126
+ job_id NUMBER,
127
+ salary NUMBER,
128
+ commission_pct NUMBER(2,2),
129
+ manager_id NUMBER(6),
130
+ department_id NUMBER(4,0),
131
+ created_at DATE
132
+ )
133
+ SQL
134
+ @conn.execute "DROP SEQUENCE test_employees_seq" rescue nil
135
+ @conn.execute <<-SQL
136
+ CREATE SEQUENCE test_employees_seq MINVALUE 1
137
+ INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE
138
+ SQL
139
+ end
140
+
141
+ after(:all) do
142
+ @conn.execute "DROP TABLE test_employees"
143
+ @conn.execute "DROP SEQUENCE test_employees_seq"
144
+ end
145
+
146
+ after(:each) do
147
+ Object.send(:remove_const, "TestEmployee")
148
+ ActiveRecord::Base.connection.clear_ignored_table_columns
149
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
150
+ end
151
+
152
+ it "should ignore specified table columns" do
153
+ class ::TestEmployee < ActiveRecord::Base
154
+ ignore_table_columns :phone_number, :hire_date
155
+ end
156
+ TestEmployee.connection.columns('test_employees').select{|c| ['phone_number','hire_date'].include?(c.name) }.should be_empty
157
+ end
158
+
159
+ it "should ignore specified table columns specified in several lines" do
160
+ class ::TestEmployee < ActiveRecord::Base
161
+ ignore_table_columns :phone_number
162
+ ignore_table_columns :hire_date
163
+ end
164
+ TestEmployee.connection.columns('test_employees').select{|c| ['phone_number','hire_date'].include?(c.name) }.should be_empty
165
+ end
166
+
167
+ it "should not ignore unspecified table columns" do
168
+ class ::TestEmployee < ActiveRecord::Base
169
+ ignore_table_columns :phone_number, :hire_date
170
+ end
171
+ TestEmployee.connection.columns('test_employees').select{|c| c.name == 'email' }.should_not be_empty
172
+ end
173
+
174
+ it "should ignore specified table columns in other connection" do
175
+ class ::TestEmployee < ActiveRecord::Base
176
+ ignore_table_columns :phone_number, :hire_date
177
+ end
178
+ # establish other connection
179
+ other_conn = ActiveRecord::Base.oracle_enhanced_connection(CONNECTION_PARAMS)
180
+ other_conn.columns('test_employees').select{|c| ['phone_number','hire_date'].include?(c.name) }.should be_empty
181
+ end
182
+
183
+ end
184
+
185
+ describe "cache table columns" do
186
+ before(:all) do
187
+ @conn = ActiveRecord::Base.connection
188
+ @conn.execute "DROP TABLE test_employees" rescue nil
189
+ @oracle11g = !! @conn.select_value("SELECT * FROM v$version WHERE banner LIKE 'Oracle%11g%'")
190
+ @conn.execute <<-SQL
191
+ CREATE TABLE test_employees (
192
+ id NUMBER PRIMARY KEY,
193
+ first_name VARCHAR2(20),
194
+ last_name VARCHAR2(25),
195
+ #{ @oracle11g ? "full_name AS (first_name || ' ' || last_name)," : "full_name VARCHAR2(46),"}
196
+ hire_date DATE
197
+ )
198
+ SQL
199
+ @conn.execute <<-SQL
200
+ CREATE TABLE test_employees_without_pk (
201
+ first_name VARCHAR2(20),
202
+ last_name VARCHAR2(25),
203
+ hire_date DATE
204
+ )
205
+ SQL
206
+ @column_names = ['id', 'first_name', 'last_name', 'full_name', 'hire_date']
207
+ @column_sql_types = ["NUMBER", "VARCHAR2(20)", "VARCHAR2(25)", "VARCHAR2(46)", "DATE"]
208
+ class ::TestEmployee < ActiveRecord::Base
209
+ end
210
+ # Another class using the same table
211
+ class ::TestEmployee2 < ActiveRecord::Base
212
+ if self.respond_to?(:table_name=)
213
+ self.table_name = "test_employees"
214
+ else
215
+ set_table_name "test_employees"
216
+ end
217
+ end
218
+ end
219
+
220
+ after(:all) do
221
+ @conn = ActiveRecord::Base.connection
222
+ Object.send(:remove_const, "TestEmployee")
223
+ Object.send(:remove_const, "TestEmployee2")
224
+ @conn.execute "DROP TABLE test_employees"
225
+ @conn.execute "DROP TABLE test_employees_without_pk"
226
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = nil
227
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
228
+ end
229
+
230
+ before(:each) do
231
+ set_logger
232
+ @conn = ActiveRecord::Base.connection
233
+ @conn.clear_columns_cache
234
+ end
235
+
236
+ after(:each) do
237
+ clear_logger
238
+ end
239
+
240
+ describe "without column caching" do
241
+
242
+ before(:each) do
243
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = false
244
+ end
245
+
246
+ it 'should identify virtual columns as such' do
247
+ pending "Not supported in this database version" unless @oracle11g
248
+ te = TestEmployee.connection.columns('test_employees').detect(&:virtual?)
249
+ te.name.should == 'full_name'
250
+ end
251
+
252
+ it "should get columns from database at first time" do
253
+ TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
254
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_cols/im
255
+ end
256
+
257
+ it "should get columns from database at second time" do
258
+ TestEmployee.connection.columns('test_employees')
259
+ @logger.clear(:debug)
260
+ TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
261
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_cols/im
262
+ end
263
+
264
+ it "should get primary key from database at first time" do
265
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
266
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
267
+ end
268
+
269
+ it "should get primary key from database at first time" do
270
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
271
+ @logger.clear(:debug)
272
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
273
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
274
+ end
275
+
276
+ it "should have correct sql types when 2 models are using the same table and AR query cache is enabled" do
277
+ @conn.cache do
278
+ TestEmployee.columns.map(&:sql_type).should == @column_sql_types
279
+ TestEmployee2.columns.map(&:sql_type).should == @column_sql_types
280
+ end
281
+ end
282
+
283
+ end
284
+
285
+ describe "with column caching" do
286
+
287
+ before(:each) do
288
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
289
+ end
290
+
291
+ it "should get columns from database at first time" do
292
+ TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
293
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_cols/im
294
+ end
295
+
296
+ it "should get columns from cache at second time" do
297
+ TestEmployee.connection.columns('test_employees')
298
+ @logger.clear(:debug)
299
+ TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
300
+ @logger.logged(:debug).last.should be_blank
301
+ end
302
+
303
+ it "should get primary key from database at first time" do
304
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
305
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
306
+ end
307
+
308
+ it "should get primary key from cache at first time" do
309
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
310
+ @logger.clear(:debug)
311
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
312
+ @logger.logged(:debug).last.should be_blank
313
+ end
314
+
315
+ it "should store primary key as nil in cache at first time for table without primary key" do
316
+ TestEmployee.connection.pk_and_sequence_for('test_employees_without_pk').should == nil
317
+ @logger.clear(:debug)
318
+ TestEmployee.connection.pk_and_sequence_for('test_employees_without_pk').should == nil
319
+ @logger.logged(:debug).last.should be_blank
320
+ end
321
+
322
+ end
323
+
324
+ end
325
+
326
+ describe "without composite_primary_keys" do
327
+
328
+ before(:all) do
329
+ @conn = ActiveRecord::Base.connection
330
+ @conn.execute "DROP TABLE test_employees" rescue nil
331
+ @conn.execute <<-SQL
332
+ CREATE TABLE test_employees (
333
+ employee_id NUMBER PRIMARY KEY,
334
+ name VARCHAR2(50)
335
+ )
336
+ SQL
337
+ Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
338
+ class ::TestEmployee < ActiveRecord::Base
339
+ if self.respond_to?(:primary_key=)
340
+ self.primary_key = :employee_id
341
+ else
342
+ set_primary_key :employee_id
343
+ end
344
+ end
345
+ end
346
+
347
+ after(:all) do
348
+ Object.send(:remove_const, "TestEmployee")
349
+ @conn.execute "DROP TABLE test_employees"
350
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
351
+ end
352
+
353
+ it "should tell ActiveRecord that count distinct is supported" do
354
+ ActiveRecord::Base.connection.supports_count_distinct?.should be_true
355
+ end
356
+
357
+ it "should execute correct SQL COUNT DISTINCT statement" do
358
+ lambda { TestEmployee.count(:employee_id, :distinct => true) }.should_not raise_error
359
+ end
360
+
361
+ end
362
+
363
+
364
+ describe "reserved words column quoting" do
365
+
366
+ before(:all) do
367
+ schema_define do
368
+ create_table :test_reserved_words do |t|
369
+ t.string :varchar2
370
+ t.integer :integer
371
+ t.text :comment
372
+ end
373
+ end
374
+ class ::TestReservedWord < ActiveRecord::Base; end
375
+ end
376
+
377
+ after(:all) do
378
+ schema_define do
379
+ drop_table :test_reserved_words
380
+ end
381
+ Object.send(:remove_const, "TestReservedWord")
382
+ ActiveRecord::Base.table_name_prefix = nil
383
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
384
+ end
385
+
386
+ before(:each) do
387
+ set_logger
388
+ end
389
+
390
+ after(:each) do
391
+ clear_logger
392
+ end
393
+
394
+ it "should create table" do
395
+ [:varchar2, :integer, :comment].each do |attr|
396
+ TestReservedWord.columns_hash[attr.to_s].name.should == attr.to_s
397
+ end
398
+ end
399
+
400
+ it "should create record" do
401
+ attrs = {
402
+ :varchar2 => 'dummy',
403
+ :integer => 1,
404
+ :comment => 'dummy'
405
+ }
406
+ record = TestReservedWord.create!(attrs)
407
+ record.reload
408
+ attrs.each do |k, v|
409
+ record.send(k).should == v
410
+ end
411
+ end
412
+
413
+ it "should remove double quotes in column quoting" do
414
+ ActiveRecord::Base.connection.quote_column_name('aaa "bbb" ccc').should == '"aaa bbb ccc"'
415
+ end
416
+
417
+ end
418
+
419
+ describe "valid table names" do
420
+ before(:all) do
421
+ @adapter = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
422
+ end
423
+
424
+ it "should be valid with letters and digits" do
425
+ @adapter.valid_table_name?("abc_123").should be_true
426
+ end
427
+
428
+ it "should be valid with schema name" do
429
+ @adapter.valid_table_name?("abc_123.def_456").should be_true
430
+ end
431
+
432
+ it "should be valid with $ in name" do
433
+ @adapter.valid_table_name?("sys.v$session").should be_true
434
+ end
435
+
436
+ it "should be valid with upcase schema name" do
437
+ @adapter.valid_table_name?("ABC_123.DEF_456").should be_true
438
+ end
439
+
440
+ it "should be valid with irregular schema name and database links" do
441
+ @adapter.valid_table_name?('abc$#_123.abc$#_123@abc$#@._123').should be_true
442
+ end
443
+
444
+ it "should not be valid with two dots in name" do
445
+ @adapter.valid_table_name?("abc_123.def_456.ghi_789").should be_false
446
+ end
447
+
448
+ it "should not be valid with invalid characters" do
449
+ @adapter.valid_table_name?("warehouse-things").should be_false
450
+ end
451
+
452
+ it "should not be valid with for camel-case" do
453
+ @adapter.valid_table_name?("Abc").should be_false
454
+ @adapter.valid_table_name?("aBc").should be_false
455
+ @adapter.valid_table_name?("abC").should be_false
456
+ end
457
+
458
+ it "should not be valid for names > 30 characters" do
459
+ @adapter.valid_table_name?("a" * 31).should be_false
460
+ end
461
+
462
+ it "should not be valid for schema names > 30 characters" do
463
+ @adapter.valid_table_name?(("a" * 31) + ".validname").should be_false
464
+ end
465
+
466
+ it "should not be valid for database links > 128 characters" do
467
+ @adapter.valid_table_name?("name@" + "a" * 129).should be_false
468
+ end
469
+
470
+ it "should not be valid for names that do not begin with alphabetic characters" do
471
+ @adapter.valid_table_name?("1abc").should be_false
472
+ @adapter.valid_table_name?("_abc").should be_false
473
+ @adapter.valid_table_name?("abc.1xyz").should be_false
474
+ @adapter.valid_table_name?("abc._xyz").should be_false
475
+ end
476
+ end
477
+
478
+ describe "table quoting" do
479
+
480
+ def create_warehouse_things_table
481
+ ActiveRecord::Schema.define do
482
+ suppress_messages do
483
+ create_table "warehouse-things" do |t|
484
+ t.string :name
485
+ t.integer :foo
486
+ end
487
+ end
488
+ end
489
+ end
490
+
491
+ def create_camel_case_table
492
+ ActiveRecord::Schema.define do
493
+ suppress_messages do
494
+ create_table "CamelCase" do |t|
495
+ t.string :name
496
+ t.integer :foo
497
+ end
498
+ end
499
+ end
500
+ end
501
+
502
+ before(:all) do
503
+ @conn = ActiveRecord::Base.connection
504
+ end
505
+
506
+ after(:each) do
507
+ ActiveRecord::Schema.define do
508
+ suppress_messages do
509
+ drop_table "warehouse-things" rescue nil
510
+ drop_table "CamelCase" rescue nil
511
+ end
512
+ end
513
+ Object.send(:remove_const, "WarehouseThing") rescue nil
514
+ Object.send(:remove_const, "CamelCase") rescue nil
515
+ end
516
+
517
+ it "should allow creation of a table with non alphanumeric characters" do
518
+ create_warehouse_things_table
519
+ class ::WarehouseThing < ActiveRecord::Base
520
+ if self.respond_to?(:table_name=)
521
+ self.table_name = "warehouse-things"
522
+ else
523
+ set_table_name "warehouse-things"
524
+ end
525
+ end
526
+
527
+ wh = WarehouseThing.create!(:name => "Foo", :foo => 2)
528
+ wh.id.should_not be_nil
529
+
530
+ @conn.tables.should include("warehouse-things")
531
+ end
532
+
533
+ it "should allow creation of a table with CamelCase name" do
534
+ create_camel_case_table
535
+ class ::CamelCase < ActiveRecord::Base
536
+ if self.respond_to?(:table_name=)
537
+ self.table_name = "CamelCase"
538
+ else
539
+ set_table_name "CamelCase"
540
+ end
541
+ end
542
+
543
+ cc = CamelCase.create!(:name => "Foo", :foo => 2)
544
+ cc.id.should_not be_nil
545
+
546
+ @conn.tables.should include("CamelCase")
547
+ end
548
+
549
+ end
550
+
551
+ describe "access table over database link" do
552
+ before(:all) do
553
+ @conn = ActiveRecord::Base.connection
554
+ @db_link = "db_link"
555
+ @sys_conn = ActiveRecord::Base.oracle_enhanced_connection(SYSTEM_CONNECTION_PARAMS)
556
+ @sys_conn.drop_table :test_posts rescue nil
557
+ @sys_conn.create_table :test_posts do |t|
558
+ t.string :title
559
+ # cannot update LOBs over database link
560
+ t.string :body
561
+ t.timestamps
562
+ end
563
+ @db_link_username = SYSTEM_CONNECTION_PARAMS[:username]
564
+ @db_link_password = SYSTEM_CONNECTION_PARAMS[:password]
565
+ @db_link_database = SYSTEM_CONNECTION_PARAMS[:database]
566
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
567
+ @conn.execute "CREATE DATABASE LINK #{@db_link} CONNECT TO #{@db_link_username} IDENTIFIED BY \"#{@db_link_password}\" USING '#{@db_link_database}'"
568
+ @conn.execute "CREATE OR REPLACE SYNONYM test_posts FOR test_posts@#{@db_link}"
569
+ @conn.execute "CREATE OR REPLACE SYNONYM test_posts_seq FOR test_posts_seq@#{@db_link}"
570
+ class ::TestPost < ActiveRecord::Base
571
+ end
572
+ if TestPost.respond_to?(:table_name=)
573
+ TestPost.table_name = "test_posts"
574
+ else
575
+ TestPost.set_table_name "test_posts"
576
+ end
577
+ end
578
+
579
+ after(:all) do
580
+ @conn.execute "DROP SYNONYM test_posts"
581
+ @conn.execute "DROP SYNONYM test_posts_seq"
582
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
583
+ @sys_conn.drop_table :test_posts rescue nil
584
+ Object.send(:remove_const, "TestPost") rescue nil
585
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
586
+ end
587
+
588
+ it "should verify database link" do
589
+ @conn.select_value("select * from dual@#{@db_link}") == 'X'
590
+ end
591
+
592
+ it "should get column names" do
593
+ TestPost.column_names.should == ["id", "title", "body", "created_at", "updated_at"]
594
+ end
595
+
596
+ it "should create record" do
597
+ p = TestPost.create(:title => "Title", :body => "Body")
598
+ p.id.should_not be_nil
599
+ TestPost.find(p.id).should_not be_nil
600
+ end
601
+
602
+ end
603
+
604
+ describe "session information" do
605
+ before(:all) do
606
+ @conn = ActiveRecord::Base.connection
607
+ end
608
+
609
+ it "should get current database name" do
610
+ # get database name if using //host:port/database connection string
611
+ database_name = CONNECTION_PARAMS[:database].split('/').last
612
+ @conn.current_database.upcase.should == database_name.upcase
613
+ end
614
+
615
+ it "should get current database session user" do
616
+ @conn.current_user.upcase.should == CONNECTION_PARAMS[:username].upcase
617
+ end
618
+ end
619
+
620
+ describe "temporary tables" do
621
+ before(:all) do
622
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:table] = 'UNUSED'
623
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:clob] = 'UNUSED'
624
+ @conn = ActiveRecord::Base.connection
625
+ end
626
+ after(:all) do
627
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces={}
628
+ end
629
+
630
+ after(:each) do
631
+ @conn.drop_table :foos rescue nil
632
+ end
633
+ it "should create ok" do
634
+ @conn.create_table :foos, :temporary => true, :id => false do |t|
635
+ t.integer :id
636
+ t.text :bar
637
+ end
638
+ end
639
+ it "should show up as temporary" do
640
+ @conn.create_table :foos, :temporary => true, :id => false do |t|
641
+ t.integer :id
642
+ end
643
+ @conn.temporary_table?("foos").should be_true
644
+ end
645
+ end
646
+
647
+ describe "eager loading" do
648
+ before(:all) do
649
+ schema_define do
650
+ create_table :test_posts do |t|
651
+ t.string :title
652
+ end
653
+ create_table :test_comments do |t|
654
+ t.integer :test_post_id
655
+ t.string :description
656
+ end
657
+ add_index :test_comments, :test_post_id
658
+ end
659
+ class ::TestPost < ActiveRecord::Base
660
+ has_many :test_comments
661
+ end
662
+ class ::TestComment < ActiveRecord::Base
663
+ belongs_to :test_post
664
+ end
665
+ @ids = (1..1010).to_a
666
+ TestPost.transaction do
667
+ @ids.each do |id|
668
+ TestPost.create!(:id => id, :title => "Title #{id}")
669
+ TestComment.create!(:test_post_id => id, :description => "Description #{id}")
670
+ end
671
+ end
672
+ end
673
+
674
+ after(:all) do
675
+ schema_define do
676
+ drop_table :test_comments
677
+ drop_table :test_posts
678
+ end
679
+ Object.send(:remove_const, "TestPost")
680
+ Object.send(:remove_const, "TestComment")
681
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
682
+ end
683
+
684
+ it "should load included association with more than 1000 records" do
685
+ posts = TestPost.includes(:test_comments).all
686
+ posts.size.should == @ids.size
687
+ end
688
+
689
+ end if ENV['RAILS_GEM_VERSION'] >= '3.1'
690
+
691
+ describe "with statement pool" do
692
+ before(:all) do
693
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS.merge(:statement_limit => 3))
694
+ @conn = ActiveRecord::Base.connection
695
+ schema_define do
696
+ drop_table :test_posts rescue nil
697
+ create_table :test_posts
698
+ end
699
+ class ::TestPost < ActiveRecord::Base
700
+ end
701
+ @statements = @conn.instance_variable_get(:@statements)
702
+ end
703
+
704
+ before(:each) do
705
+ @conn.clear_cache!
706
+ end
707
+
708
+ after(:all) do
709
+ schema_define do
710
+ drop_table :test_posts
711
+ end
712
+ Object.send(:remove_const, "TestPost")
713
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
714
+ end
715
+
716
+ it "should clear older cursors when statement limit is reached" do
717
+ pk = TestPost.columns.find { |c| c.primary }
718
+ sub = @conn.substitute_at(pk, 0)
719
+ binds = [[pk, 1]]
720
+
721
+ lambda {
722
+ 4.times do |i|
723
+ @conn.exec_query("SELECT * FROM test_posts WHERE #{i}=#{i} AND id = #{sub}", "SQL", binds)
724
+ end
725
+ }.should change(@statements, :length).by(+3)
726
+ end
727
+
728
+ it "should cache UPDATE statements with bind variables" do
729
+ lambda {
730
+ pk = TestPost.columns.find { |c| c.primary }
731
+ sub = @conn.substitute_at(pk, 0)
732
+ binds = [[pk, 1]]
733
+ @conn.exec_update("UPDATE test_posts SET id = #{sub}", "SQL", binds)
734
+ }.should change(@statements, :length).by(+1)
735
+ end
736
+
737
+ it "should not cache UPDATE statements without bind variables" do
738
+ lambda {
739
+ binds = []
740
+ @conn.exec_update("UPDATE test_posts SET id = 1", "SQL", binds)
741
+ }.should_not change(@statements, :length)
742
+ end
743
+ end if ENV['RAILS_GEM_VERSION'] >= '3.1'
744
+
745
+ describe "explain" do
746
+ before(:all) do
747
+ @conn = ActiveRecord::Base.connection
748
+ schema_define do
749
+ drop_table :test_posts rescue nil
750
+ create_table :test_posts
751
+ end
752
+ class ::TestPost < ActiveRecord::Base
753
+ end
754
+ end
755
+
756
+ after(:all) do
757
+ schema_define do
758
+ drop_table :test_posts
759
+ end
760
+ Object.send(:remove_const, "TestPost")
761
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
762
+ end
763
+
764
+ it "should explain query" do
765
+ explain = TestPost.where(:id => 1).explain
766
+ explain.should include("Cost")
767
+ explain.should include("INDEX UNIQUE SCAN")
768
+ end
769
+
770
+ it "should explain query with binds" do
771
+ pk = TestPost.columns.find { |c| c.primary }
772
+ sub = @conn.substitute_at(pk, 0)
773
+ explain = TestPost.where(TestPost.arel_table[pk.name].eq(sub)).bind([pk, 1]).explain
774
+ explain.should include("Cost")
775
+ explain.should include("INDEX UNIQUE SCAN")
776
+ end
777
+ end if ENV['RAILS_GEM_VERSION'] >= '3.2'
778
+ end