activerecord-oracle_enhanced-adapter 6.0.6 → 6.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +128 -4
- data/README.md +12 -1
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +2 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +0 -9
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +6 -6
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +8 -9
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +7 -5
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +1 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +2 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +7 -11
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +2 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +15 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +42 -39
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +16 -17
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +52 -33
- data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
- data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
- data/lib/arel/visitors/oracle.rb +217 -0
- data/lib/arel/visitors/oracle12.rb +124 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +9 -3
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +0 -1
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +27 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +83 -0
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +4 -2
- data/spec/spec_config.yaml.template +2 -2
- data/spec/spec_helper.rb +13 -2
- metadata +6 -4
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class Oracle12 < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
8
|
+
# Oracle does not allow LIMIT clause with select for update
|
9
|
+
if o.limit && o.lock
|
10
|
+
raise ArgumentError, <<~MSG
|
11
|
+
Combination of limit and lock is not supported. Because generated SQL statements
|
12
|
+
`SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.
|
13
|
+
MSG
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_Arel_Nodes_SelectOptions(o, collector)
|
19
|
+
collector = maybe_visit o.offset, collector
|
20
|
+
collector = maybe_visit o.limit, collector
|
21
|
+
maybe_visit o.lock, collector
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
25
|
+
collector << "FETCH FIRST "
|
26
|
+
collector = visit o.expr, collector
|
27
|
+
collector << " ROWS ONLY"
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
31
|
+
collector << "OFFSET "
|
32
|
+
visit o.expr, collector
|
33
|
+
collector << " ROWS"
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_Arel_Nodes_Except(o, collector)
|
37
|
+
collector << "( "
|
38
|
+
collector = infix_value o, collector, " MINUS "
|
39
|
+
collector << " )"
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# To avoid ORA-01795: maximum number of expressions in a list is 1000
|
44
|
+
# tell ActiveRecord to limit us to 1000 ids at a time
|
45
|
+
def visit_Arel_Nodes_HomogeneousIn(o, collector)
|
46
|
+
in_clause_length = @connection.in_clause_length
|
47
|
+
values = o.casted_values.map { |v| @connection.quote(v) }
|
48
|
+
column_name = quote_table_name(o.table_name) + "." + quote_column_name(o.column_name)
|
49
|
+
operator =
|
50
|
+
if o.type == :in
|
51
|
+
" IN ("
|
52
|
+
else
|
53
|
+
" NOT IN ("
|
54
|
+
end
|
55
|
+
|
56
|
+
if !Array === values || values.length <= in_clause_length
|
57
|
+
collector << column_name
|
58
|
+
collector << operator
|
59
|
+
|
60
|
+
expr =
|
61
|
+
if values.empty?
|
62
|
+
@connection.quote(nil)
|
63
|
+
else
|
64
|
+
values.join(",")
|
65
|
+
end
|
66
|
+
|
67
|
+
collector << expr
|
68
|
+
collector << ")"
|
69
|
+
else
|
70
|
+
separator =
|
71
|
+
if o.type == :in
|
72
|
+
" OR "
|
73
|
+
else
|
74
|
+
" AND "
|
75
|
+
end
|
76
|
+
collector << "("
|
77
|
+
values.each_slice(in_clause_length).each_with_index do |valuez, i|
|
78
|
+
collector << separator unless i == 0
|
79
|
+
collector << column_name
|
80
|
+
collector << operator
|
81
|
+
collector << valuez.join(",")
|
82
|
+
collector << ")"
|
83
|
+
end
|
84
|
+
collector << ")"
|
85
|
+
end
|
86
|
+
|
87
|
+
collector
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
91
|
+
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
|
92
|
+
if o.orders.any? && o.limit.nil?
|
93
|
+
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
|
94
|
+
# otherwise let the user deal with the error
|
95
|
+
o = o.dup
|
96
|
+
o.orders = []
|
97
|
+
end
|
98
|
+
|
99
|
+
super
|
100
|
+
end
|
101
|
+
|
102
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
103
|
+
collector.add_bind(o.value) { |i| ":a#{i}" }
|
104
|
+
end
|
105
|
+
|
106
|
+
def is_distinct_from(o, collector)
|
107
|
+
collector << "DECODE("
|
108
|
+
collector = visit [o.left, o.right, 0, 1], collector
|
109
|
+
collector << ")"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Oracle will occur an error `ORA-00907: missing right parenthesis`
|
113
|
+
# when using `ORDER BY` in `UPDATE` or `DELETE`'s subquery.
|
114
|
+
#
|
115
|
+
# This method has been overridden based on the following code.
|
116
|
+
# https://github.com/rails/rails/blob/v6.1.0.rc1/activerecord/lib/arel/visitors/to_sql.rb#L815-L825
|
117
|
+
def build_subselect(key, o)
|
118
|
+
stmt = super
|
119
|
+
stmt.orders = [] # `orders` will never be set to prevent `ORA-00907`.
|
120
|
+
stmt
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -40,6 +40,12 @@ describe "OracleEnhancedAdapter establish connection" do
|
|
40
40
|
ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(cursor_sharing: :exact))
|
41
41
|
expect(ActiveRecord::Base.connection.select_value("select value from v$parameter where name = 'cursor_sharing'")).to eq("EXACT")
|
42
42
|
end
|
43
|
+
|
44
|
+
it "should connect to database using service_name" do
|
45
|
+
ActiveRecord::Base.establish_connection(SERVICE_NAME_CONNECTION_PARAMS)
|
46
|
+
expect(ActiveRecord::Base.connection).not_to be_nil
|
47
|
+
expect(ActiveRecord::Base.connection.class).to eq(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
|
48
|
+
end
|
43
49
|
end
|
44
50
|
|
45
51
|
describe "OracleEnhancedConnection" do
|
@@ -81,13 +87,13 @@ describe "OracleEnhancedConnection" do
|
|
81
87
|
expect(ActiveRecord::Base.connection).to be_active
|
82
88
|
end
|
83
89
|
|
84
|
-
it "should
|
90
|
+
it "should switch to specified schema" do
|
85
91
|
ActiveRecord::Base.establish_connection(CONNECTION_WITH_SCHEMA_PARAMS)
|
86
92
|
expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
|
87
93
|
expect(ActiveRecord::Base.connection.current_user).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:username].upcase)
|
88
94
|
end
|
89
95
|
|
90
|
-
it "should
|
96
|
+
it "should switch to specified schema after reset" do
|
91
97
|
ActiveRecord::Base.connection.reset!
|
92
98
|
expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
|
93
99
|
end
|
@@ -180,7 +186,7 @@ describe "OracleEnhancedConnection" do
|
|
180
186
|
describe "with slash-prefixed database name (service name)" do
|
181
187
|
before(:all) do
|
182
188
|
params = CONNECTION_PARAMS.dup
|
183
|
-
params[:database] = "/#{params[:database]}" unless params[:database].
|
189
|
+
params[:database] = "/#{params[:database]}" unless params[:database].start_with?("/")
|
184
190
|
@conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
|
185
191
|
end
|
186
192
|
|
@@ -20,6 +20,11 @@ describe "Oracle Enhanced adapter database tasks" do
|
|
20
20
|
query = "SELECT COUNT(*) FROM dba_users WHERE UPPER(username) = '#{new_user_config[:username].upcase}'"
|
21
21
|
expect(ActiveRecord::Base.connection.select_value(query)).to eq(1)
|
22
22
|
end
|
23
|
+
it "grants permissions defined by OracleEnhancedAdapter.persmissions" do
|
24
|
+
query = "SELECT COUNT(*) FROM DBA_SYS_PRIVS WHERE GRANTEE = '#{new_user_config[:username].upcase}'"
|
25
|
+
permissions_count = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.permissions.size
|
26
|
+
expect(ActiveRecord::Base.connection.select_value(query)).to eq(permissions_count)
|
27
|
+
end
|
23
28
|
after do
|
24
29
|
ActiveRecord::Base.connection.execute("DROP USER #{new_user_config[:username]}")
|
25
30
|
end
|
@@ -305,6 +305,33 @@ describe "OracleEnhancedAdapter schema dump" do
|
|
305
305
|
end
|
306
306
|
end
|
307
307
|
|
308
|
+
describe "context indexes" do
|
309
|
+
before(:each) do
|
310
|
+
schema_define do
|
311
|
+
create_table :test_context_indexed_posts, force: true do |t|
|
312
|
+
t.string :title
|
313
|
+
t.string :body
|
314
|
+
t.index :title
|
315
|
+
end
|
316
|
+
add_context_index :test_context_indexed_posts, :body, sync: "ON COMMIT"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
after(:each) do
|
321
|
+
schema_define do
|
322
|
+
drop_table :test_context_indexed_posts
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should dump the context index" do
|
327
|
+
expect(standard_dump).to include(%(add_context_index "test_context_indexed_posts", ["body"]))
|
328
|
+
end
|
329
|
+
|
330
|
+
it "dumps the sync option" do
|
331
|
+
expect(standard_dump).to include(%(sync: "ON COMMIT"))
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
308
335
|
describe "virtual columns" do
|
309
336
|
before(:all) do
|
310
337
|
skip "Not supported in this database version" unless @oracle11g_or_higher
|
@@ -232,6 +232,46 @@ describe "OracleEnhancedAdapter" do
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
describe "lists" do
|
236
|
+
before(:all) do
|
237
|
+
schema_define do
|
238
|
+
create_table :test_posts do |t|
|
239
|
+
t.string :title
|
240
|
+
end
|
241
|
+
end
|
242
|
+
class ::TestPost < ActiveRecord::Base
|
243
|
+
has_many :test_comments
|
244
|
+
end
|
245
|
+
@ids = (1..1010).to_a
|
246
|
+
TestPost.transaction do
|
247
|
+
@ids.each do |id|
|
248
|
+
TestPost.create!(id: id, title: "Title #{id}")
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
after(:all) do
|
254
|
+
schema_define do
|
255
|
+
drop_table :test_posts
|
256
|
+
end
|
257
|
+
Object.send(:remove_const, "TestPost")
|
258
|
+
ActiveRecord::Base.clear_cache!
|
259
|
+
end
|
260
|
+
|
261
|
+
##
|
262
|
+
# See this GitHub issue for an explanation of homogenous lists.
|
263
|
+
# https://github.com/rails/rails/commit/72fd0bae5948c1169411941aeea6fef4c58f34a9
|
264
|
+
it "should allow more than 1000 items in a list where the list is homogenous" do
|
265
|
+
posts = TestPost.where(id: @ids).to_a
|
266
|
+
expect(posts.size).to eq(@ids.size)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should allow more than 1000 items in a list where the list is non-homogenous" do
|
270
|
+
posts = TestPost.where(id: [*@ids, nil]).to_a
|
271
|
+
expect(posts.size).to eq(@ids.size)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
235
275
|
describe "with statement pool" do
|
236
276
|
before(:all) do
|
237
277
|
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS.merge(statement_limit: 3))
|
@@ -283,6 +323,14 @@ describe "OracleEnhancedAdapter" do
|
|
283
323
|
end
|
284
324
|
end
|
285
325
|
|
326
|
+
describe "database_exists?" do
|
327
|
+
it "should raise `NotImplementedError`" do
|
328
|
+
expect {
|
329
|
+
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.database_exists?(CONNECTION_PARAMS)
|
330
|
+
}.to raise_error(NotImplementedError)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
286
334
|
describe "explain" do
|
287
335
|
before(:all) do
|
288
336
|
@conn = ActiveRecord::Base.connection
|
@@ -698,4 +746,39 @@ describe "OracleEnhancedAdapter" do
|
|
698
746
|
expect(post.explain).to include("| TABLE ACCESS FULL| TEST_POSTS |")
|
699
747
|
end
|
700
748
|
end
|
749
|
+
|
750
|
+
describe "homogeneous in" do
|
751
|
+
before(:all) do
|
752
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
753
|
+
@conn = ActiveRecord::Base.connection
|
754
|
+
schema_define do
|
755
|
+
create_table :test_posts, force: true
|
756
|
+
create_table :test_comments, force: true do |t|
|
757
|
+
t.integer :test_post_id
|
758
|
+
end
|
759
|
+
end
|
760
|
+
class ::TestPost < ActiveRecord::Base
|
761
|
+
has_many :test_comments
|
762
|
+
end
|
763
|
+
class ::TestComment < ActiveRecord::Base
|
764
|
+
belongs_to :test_post
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
after(:all) do
|
769
|
+
schema_define do
|
770
|
+
drop_table :test_posts, if_exists: true
|
771
|
+
drop_table :test_comments, if_exists: true
|
772
|
+
end
|
773
|
+
Object.send(:remove_const, "TestPost")
|
774
|
+
Object.send(:remove_const, "TestComment")
|
775
|
+
ActiveRecord::Base.clear_cache!
|
776
|
+
end
|
777
|
+
|
778
|
+
it "should not raise undefined method length" do
|
779
|
+
post = TestPost.create!
|
780
|
+
post.test_comments << TestComment.create!
|
781
|
+
expect(TestComment.where(test_post_id: TestPost.select(:id)).size).to eq(1)
|
782
|
+
end
|
783
|
+
end
|
701
784
|
end
|
@@ -35,9 +35,11 @@ describe "OracleEnhancedAdapter quoting of NCHAR and NVARCHAR2 columns" do
|
|
35
35
|
columns = @conn.columns("test_items")
|
36
36
|
%w(nchar_column nvarchar2_column char_column varchar2_column).each do |col|
|
37
37
|
column = columns.detect { |c| c.name == col }
|
38
|
-
|
38
|
+
type = @conn.lookup_cast_type_from_column(column)
|
39
|
+
value = type.serialize("abc")
|
39
40
|
expect(@conn.quote(value)).to eq(column.sql_type[0, 1] == "N" ? "N'abc'" : "'abc'")
|
40
|
-
|
41
|
+
type = @conn.lookup_cast_type_from_column(column)
|
42
|
+
nilvalue = type.serialize(nil)
|
41
43
|
expect(@conn.quote(nilvalue)).to eq("NULL")
|
42
44
|
end
|
43
45
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# copy this file to spec/
|
1
|
+
# copy this file to spec/spec_config.yaml and set appropriate values
|
2
2
|
# you can also use environment variables, see spec_helper.rb
|
3
3
|
database:
|
4
4
|
name: 'orcl'
|
@@ -8,4 +8,4 @@ database:
|
|
8
8
|
password: 'oracle_enhanced'
|
9
9
|
sys_password: 'admin'
|
10
10
|
non_default_tablespace: 'SYSTEM'
|
11
|
-
timezone: 'Europe/Riga'
|
11
|
+
timezone: 'Europe/Riga'
|
data/spec/spec_helper.rb
CHANGED
@@ -17,8 +17,8 @@ end
|
|
17
17
|
|
18
18
|
require "rspec"
|
19
19
|
|
20
|
-
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
21
|
-
puts "==> Running specs with
|
20
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby"
|
21
|
+
puts "==> Running specs with ruby version #{RUBY_VERSION}"
|
22
22
|
require "oci8"
|
23
23
|
elsif RUBY_ENGINE == "jruby"
|
24
24
|
puts "==> Running specs with JRuby version #{JRUBY_VERSION}"
|
@@ -105,6 +105,8 @@ module LoggerSpecHelper
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
+
ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.replace(["EXPLAIN"])
|
109
|
+
|
108
110
|
module SchemaSpecHelper
|
109
111
|
def schema_define(&block)
|
110
112
|
ActiveRecord::Schema.define do
|
@@ -183,6 +185,15 @@ SYSTEM_CONNECTION_PARAMS = {
|
|
183
185
|
password: DATABASE_SYS_PASSWORD
|
184
186
|
}
|
185
187
|
|
188
|
+
SERVICE_NAME_CONNECTION_PARAMS = {
|
189
|
+
adapter: "oracle_enhanced",
|
190
|
+
database: "/#{DATABASE_NAME}",
|
191
|
+
host: DATABASE_HOST,
|
192
|
+
port: DATABASE_PORT,
|
193
|
+
username: DATABASE_USER,
|
194
|
+
password: DATABASE_PASSWORD
|
195
|
+
}
|
196
|
+
|
186
197
|
DATABASE_NON_DEFAULT_TABLESPACE = config["database"]["non_default_tablespace"] || ENV["DATABASE_NON_DEFAULT_TABLESPACE"] || "SYSTEM"
|
187
198
|
|
188
199
|
# set default time zone in TZ environment variable
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-oracle_enhanced-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Raimonds Simanovskis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01
|
11
|
+
date: 2021-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: 6.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: 6.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: ruby-plsql
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +86,8 @@ files:
|
|
86
86
|
- lib/active_record/type/oracle_enhanced/timestampltz.rb
|
87
87
|
- lib/active_record/type/oracle_enhanced/timestamptz.rb
|
88
88
|
- lib/activerecord-oracle_enhanced-adapter.rb
|
89
|
+
- lib/arel/visitors/oracle.rb
|
90
|
+
- lib/arel/visitors/oracle12.rb
|
89
91
|
- spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb
|
90
92
|
- spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb
|
91
93
|
- spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb
|