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.
- data/.rspec +2 -0
- data/Gemfile +52 -0
- data/History.md +301 -0
- data/License.txt +20 -0
- data/README.md +123 -0
- data/RUNNING_TESTS.md +45 -0
- data/Rakefile +59 -0
- data/VERSION +1 -0
- data/activerecord-oracle_enhanced-adapter-with-schema.gemspec +130 -0
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1399 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +121 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +146 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +119 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +46 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +494 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +227 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +260 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +428 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +258 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
- data/lib/activerecord-oracle_enhanced-adapter-with-schema.rb +25 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +778 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +332 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +427 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +19 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +113 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +1388 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +69 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +141 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +378 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +440 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1385 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
- data/spec/spec_helper.rb +189 -0
- 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
|