activerecord-oracle_enhanced-adapter 8.1.0-java
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 +7 -0
- data/History.md +1971 -0
- data/License.txt +20 -0
- data/README.md +947 -0
- data/VERSION +1 -0
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +7 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +24 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +137 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +359 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +47 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +325 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +63 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +71 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +629 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +57 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +465 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +195 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +186 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +99 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +197 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +739 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +394 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +3 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +886 -0
- data/lib/active_record/type/oracle_enhanced/boolean.rb +19 -0
- data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +14 -0
- data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
- data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
- data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
- data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
- data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
- data/lib/activerecord-oracle_enhanced-adapter.rb +25 -0
- data/lib/arel/visitors/oracle.rb +216 -0
- data/lib/arel/visitors/oracle12.rb +121 -0
- data/lib/arel/visitors/oracle_common.rb +51 -0
- data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +24 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb +40 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb +84 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +589 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +431 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +122 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb +63 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +69 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +362 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +181 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1318 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +485 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +815 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +230 -0
- data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +206 -0
- data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +67 -0
- data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
- data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +141 -0
- data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +101 -0
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +56 -0
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
- data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
- data/spec/active_record/oracle_enhanced/type/raw_spec.rb +137 -0
- data/spec/active_record/oracle_enhanced/type/text_spec.rb +295 -0
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +107 -0
- data/spec/spec_config.yaml.template +11 -0
- data/spec/spec_helper.rb +225 -0
- data/spec/support/alter_system_set_open_cursors.sql +1 -0
- data/spec/support/alter_system_user_password.sql +2 -0
- data/spec/support/create_oracle_enhanced_users.sql +31 -0
- metadata +181 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter handling of DECIMAL columns" do
|
|
4
|
+
include SchemaSpecHelper
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
8
|
+
@conn = ActiveRecord::Base.connection
|
|
9
|
+
schema_define do
|
|
10
|
+
create_table :test2_employees, force: true do |t|
|
|
11
|
+
t.string :first_name, limit: 20
|
|
12
|
+
t.string :last_name, limit: 25
|
|
13
|
+
t.string :email, limit: 25
|
|
14
|
+
t.string :phone_number, limit: 25
|
|
15
|
+
t.date :hire_date
|
|
16
|
+
t.integer :job_id
|
|
17
|
+
t.integer :salary
|
|
18
|
+
t.decimal :commission_pct, scale: 2, precision: 2
|
|
19
|
+
t.decimal :hourly_rate
|
|
20
|
+
t.integer :manager_id, limit: 6
|
|
21
|
+
t.integer :is_manager, limit: 1
|
|
22
|
+
t.decimal :department_id, scale: 0, precision: 4
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
after(:all) do
|
|
31
|
+
Object.send(:remove_const, "Test2Employee")
|
|
32
|
+
@conn.drop_table :test2_employees, if_exists: true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should set DECIMAL column type as decimal" do
|
|
36
|
+
columns = @conn.columns("test2_employees")
|
|
37
|
+
column = columns.detect { |c| c.name == "hourly_rate" }
|
|
38
|
+
expect(column.type).to eq(:decimal)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should DECIMAL column type returns an exact value" do
|
|
42
|
+
employee = Test2Employee.create(hourly_rate: 4.40125)
|
|
43
|
+
|
|
44
|
+
employee.reload
|
|
45
|
+
|
|
46
|
+
expect(employee.hourly_rate).to eq(4.40125)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should DECIMAL column type rounds if scale is specified and value exceeds scale" do
|
|
50
|
+
employee = Test2Employee.create(commission_pct: 0.1575)
|
|
51
|
+
|
|
52
|
+
employee.reload
|
|
53
|
+
|
|
54
|
+
expect(employee.commission_pct).to eq(0.16)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter dirty object tracking" do
|
|
4
|
+
include SchemaSpecHelper
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
8
|
+
schema_define do
|
|
9
|
+
create_table :test_employees, force: true do |t|
|
|
10
|
+
t.string :first_name, limit: 20
|
|
11
|
+
t.string :last_name, limit: 25
|
|
12
|
+
t.integer :job_id, limit: 6, null: true
|
|
13
|
+
t.decimal :salary, precision: 8, scale: 2
|
|
14
|
+
t.text :comments
|
|
15
|
+
t.date :hire_date
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class TestEmployee < ActiveRecord::Base
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
after(:all) do
|
|
24
|
+
schema_define do
|
|
25
|
+
drop_table :test_employees
|
|
26
|
+
end
|
|
27
|
+
Object.send(:remove_const, "TestEmployee")
|
|
28
|
+
ActiveRecord::Base.clear_cache!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should not mark empty string (stored as NULL) as changed when reassigning it" do
|
|
32
|
+
@employee = TestEmployee.create!(first_name: "")
|
|
33
|
+
@employee.first_name = ""
|
|
34
|
+
expect(@employee).not_to be_changed
|
|
35
|
+
@employee.reload
|
|
36
|
+
@employee.first_name = ""
|
|
37
|
+
expect(@employee).not_to be_changed
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should not mark empty integer (stored as NULL) as changed when reassigning it" do
|
|
41
|
+
@employee = TestEmployee.create!(job_id: "")
|
|
42
|
+
@employee.job_id = ""
|
|
43
|
+
expect(@employee).not_to be_changed
|
|
44
|
+
@employee.reload
|
|
45
|
+
@employee.job_id = ""
|
|
46
|
+
expect(@employee).not_to be_changed
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should not mark empty decimal (stored as NULL) as changed when reassigning it" do
|
|
50
|
+
@employee = TestEmployee.create!(salary: "")
|
|
51
|
+
@employee.salary = ""
|
|
52
|
+
expect(@employee).not_to be_changed
|
|
53
|
+
@employee.reload
|
|
54
|
+
@employee.salary = ""
|
|
55
|
+
expect(@employee).not_to be_changed
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should not mark empty text (stored as NULL) as changed when reassigning it" do
|
|
59
|
+
@employee = TestEmployee.create!(comments: nil)
|
|
60
|
+
@employee.comments = nil
|
|
61
|
+
expect(@employee).not_to be_changed
|
|
62
|
+
@employee.reload
|
|
63
|
+
@employee.comments = nil
|
|
64
|
+
expect(@employee).not_to be_changed
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should not mark empty text (stored as empty_clob()) as changed when reassigning it" do
|
|
68
|
+
@employee = TestEmployee.create!(comments: "")
|
|
69
|
+
@employee.comments = ""
|
|
70
|
+
expect(@employee).not_to be_changed
|
|
71
|
+
@employee.reload
|
|
72
|
+
@employee.comments = ""
|
|
73
|
+
expect(@employee).not_to be_changed
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should mark empty text (stored as empty_clob()) as changed when assigning nil to it" do
|
|
77
|
+
@employee = TestEmployee.create!(comments: "")
|
|
78
|
+
@employee.comments = nil
|
|
79
|
+
expect(@employee).to be_changed
|
|
80
|
+
@employee.reload
|
|
81
|
+
@employee.comments = nil
|
|
82
|
+
expect(@employee).to be_changed
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should mark empty text (stored as NULL) as changed when assigning '' to it" do
|
|
86
|
+
@employee = TestEmployee.create!(comments: nil)
|
|
87
|
+
@employee.comments = ""
|
|
88
|
+
expect(@employee).to be_changed
|
|
89
|
+
@employee.reload
|
|
90
|
+
@employee.comments = ""
|
|
91
|
+
expect(@employee).to be_changed
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should not mark empty date (stored as NULL) as changed when reassigning it" do
|
|
95
|
+
@employee = TestEmployee.create!(hire_date: "")
|
|
96
|
+
@employee.hire_date = ""
|
|
97
|
+
expect(@employee).not_to be_changed
|
|
98
|
+
@employee.reload
|
|
99
|
+
@employee.hire_date = ""
|
|
100
|
+
expect(@employee).not_to be_changed
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should not mark integer as changed when reassigning it" do
|
|
104
|
+
@employee = TestEmployee.new
|
|
105
|
+
@employee.job_id = 0
|
|
106
|
+
expect(@employee.save!).to be_truthy
|
|
107
|
+
|
|
108
|
+
expect(@employee).not_to be_changed
|
|
109
|
+
|
|
110
|
+
@employee.job_id = "0"
|
|
111
|
+
expect(@employee).not_to be_changed
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should not update unchanged CLOBs" do
|
|
115
|
+
@conn = nil
|
|
116
|
+
@raw_connection = nil
|
|
117
|
+
@employee = TestEmployee.create!(
|
|
118
|
+
comments: "initial"
|
|
119
|
+
)
|
|
120
|
+
expect(@employee.save!).to be_truthy
|
|
121
|
+
@employee.reload
|
|
122
|
+
expect(@employee.comments).to eq("initial")
|
|
123
|
+
|
|
124
|
+
oci_conn = @conn.instance_variable_get("@raw_connection")
|
|
125
|
+
class << oci_conn
|
|
126
|
+
def write_lob(lob, value, is_binary = false); raise "don't do this'"; end
|
|
127
|
+
end
|
|
128
|
+
@employee.comments = +"initial"
|
|
129
|
+
expect(@employee.comments_changed?).to be false
|
|
130
|
+
expect { @employee.save! }.not_to raise_error
|
|
131
|
+
class << oci_conn
|
|
132
|
+
remove_method :write_lob
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "should be able to handle attributes which are not backed by a column" do
|
|
137
|
+
TestEmployee.create!(comments: "initial")
|
|
138
|
+
@employee = TestEmployee.select("#{TestEmployee.quoted_table_name}.*, 24 ranking").first
|
|
139
|
+
expect { @employee.ranking = 25 }.to_not raise_error
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter handling of BINARY_FLOAT columns" do
|
|
4
|
+
include SchemaSpecHelper
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
8
|
+
@conn = ActiveRecord::Base.connection
|
|
9
|
+
schema_define do
|
|
10
|
+
create_table :test2_employees, force: true do |t|
|
|
11
|
+
t.string :first_name, limit: 20
|
|
12
|
+
t.string :last_name, limit: 25
|
|
13
|
+
t.string :email, limit: 25
|
|
14
|
+
t.string :phone_number, limit: 25
|
|
15
|
+
t.date :hire_date
|
|
16
|
+
t.integer :job_id
|
|
17
|
+
t.integer :salary
|
|
18
|
+
t.decimal :commission_pct, scale: 2, precision: 2
|
|
19
|
+
t.float :hourly_rate
|
|
20
|
+
t.integer :manager_id, limit: 6
|
|
21
|
+
t.integer :is_manager, limit: 1
|
|
22
|
+
t.decimal :department_id, scale: 0, precision: 4
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
after(:all) do
|
|
31
|
+
Object.send(:remove_const, "Test2Employee")
|
|
32
|
+
@conn.drop_table :test2_employees, if_exists: true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should set BINARY_FLOAT column type as float" do
|
|
36
|
+
columns = @conn.columns("test2_employees")
|
|
37
|
+
column = columns.detect { |c| c.name == "hourly_rate" }
|
|
38
|
+
expect(column.type).to eq(:float)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should BINARY_FLOAT column type returns an approximate value" do
|
|
42
|
+
employee = Test2Employee.create(hourly_rate: 4.4)
|
|
43
|
+
|
|
44
|
+
employee.reload
|
|
45
|
+
|
|
46
|
+
expect(employee.hourly_rate).to eq(4.400000095367432)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter integer type detection based on attribute settings" do
|
|
4
|
+
before(:all) do
|
|
5
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
6
|
+
conn = ActiveRecord::Base.lease_connection
|
|
7
|
+
conn.execute "DROP TABLE test2_employees" rescue nil
|
|
8
|
+
conn.execute <<~SQL
|
|
9
|
+
CREATE TABLE test2_employees (
|
|
10
|
+
id NUMBER PRIMARY KEY,
|
|
11
|
+
first_name VARCHAR2(20),
|
|
12
|
+
last_name VARCHAR2(25),
|
|
13
|
+
email VARCHAR2(25),
|
|
14
|
+
phone_number VARCHAR2(20),
|
|
15
|
+
hire_date DATE,
|
|
16
|
+
job_id NUMBER,
|
|
17
|
+
salary NUMBER,
|
|
18
|
+
commission_pct NUMBER(2,2),
|
|
19
|
+
manager_id NUMBER(6),
|
|
20
|
+
is_manager NUMBER(1),
|
|
21
|
+
department_id NUMBER(4,0),
|
|
22
|
+
created_at DATE
|
|
23
|
+
)
|
|
24
|
+
SQL
|
|
25
|
+
conn.execute "DROP SEQUENCE test2_employees_seq" rescue nil
|
|
26
|
+
conn.execute <<~SQL
|
|
27
|
+
CREATE SEQUENCE test2_employees_seq MINVALUE 1
|
|
28
|
+
INCREMENT BY 1 START WITH 10040 CACHE 20 NOORDER NOCYCLE
|
|
29
|
+
SQL
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
after(:all) do
|
|
33
|
+
conn = ActiveRecord::Base.lease_connection
|
|
34
|
+
conn.execute "DROP TABLE test2_employees"
|
|
35
|
+
conn.execute "DROP SEQUENCE test2_employees_seq"
|
|
36
|
+
ActiveRecord::Base.release_connection
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "/ NUMBER values from ActiveRecord model" do
|
|
40
|
+
before(:each) do
|
|
41
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after(:each) do
|
|
46
|
+
Object.send(:remove_const, "Test2Employee")
|
|
47
|
+
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = true
|
|
48
|
+
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.clear_type_map!
|
|
49
|
+
ActiveRecord::Base.clear_cache!
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def create_employee2
|
|
53
|
+
@employee2 = Test2Employee.create(
|
|
54
|
+
first_name: "First",
|
|
55
|
+
last_name: "Last",
|
|
56
|
+
job_id: 1,
|
|
57
|
+
is_manager: 1,
|
|
58
|
+
salary: 1000
|
|
59
|
+
)
|
|
60
|
+
@employee2.reload
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should return BigDecimal value from NUMBER column if by default" do
|
|
64
|
+
create_employee2
|
|
65
|
+
expect(@employee2.job_id.class).to eq(BigDecimal)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should return Integer value from NUMBER column if attribute is set to integer" do
|
|
69
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
70
|
+
attribute :job_id, :integer
|
|
71
|
+
end
|
|
72
|
+
create_employee2
|
|
73
|
+
expect(@employee2.job_id).to be_a(Integer)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should return Integer value from NUMBER column with integer value using _before_type_cast method" do
|
|
77
|
+
create_employee2
|
|
78
|
+
expect(@employee2.job_id_before_type_cast).to be_a(Integer)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should return Boolean value from NUMBER(1) column if emulate booleans is used" do
|
|
82
|
+
create_employee2
|
|
83
|
+
expect(@employee2.is_manager.class).to eq(TrueClass)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should return Integer value from NUMBER(1) column if attribute is set to integer" do
|
|
87
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
88
|
+
attribute :is_manager, :integer
|
|
89
|
+
end
|
|
90
|
+
create_employee2
|
|
91
|
+
expect(@employee2.is_manager).to be_a(Integer)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should return Integer value from NUMBER(1) column if emulate_booleans is set to false" do
|
|
95
|
+
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans = false
|
|
96
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
97
|
+
create_employee2
|
|
98
|
+
expect(@employee2.is_manager).to be_a(Integer)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter attribute API support for JSON type" do
|
|
4
|
+
include SchemaSpecHelper
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
8
|
+
@conn = ActiveRecord::Base.connection
|
|
9
|
+
@oracle12c_or_higher = !! @conn.select_value(
|
|
10
|
+
"select * from product_component_version where product like 'Oracle%' and to_number(substr(version,1,2)) >= 12")
|
|
11
|
+
skip "Not supported in this database version" unless @oracle12c_or_higher
|
|
12
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
13
|
+
schema_define do
|
|
14
|
+
create_table :test_posts, force: true do |t|
|
|
15
|
+
t.string :title
|
|
16
|
+
t.text :article
|
|
17
|
+
end
|
|
18
|
+
execute "alter table test_posts add constraint test_posts_title_is_json check (title is json)"
|
|
19
|
+
execute "alter table test_posts add constraint test_posts_article_is_json check (article is json)"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class ::TestPost < ActiveRecord::Base
|
|
23
|
+
attribute :title, :json
|
|
24
|
+
attribute :article, :json
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
after(:all) do
|
|
29
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
30
|
+
schema_define do
|
|
31
|
+
drop_table :test_posts, if_exists: true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
before(:each) do
|
|
36
|
+
TestPost.delete_all
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should support attribute api for JSON" do
|
|
40
|
+
post = TestPost.create!(title: { "publish" => true, "foo" => "bar" }, article: { "bar" => "baz" })
|
|
41
|
+
post.reload
|
|
42
|
+
expect(post.title).to eq ({ "publish" => true, "foo" => "bar" })
|
|
43
|
+
expect(post.article).to eq ({ "bar" => "baz" })
|
|
44
|
+
post.title = ({ "publish" => false, "foo" => "bar2" })
|
|
45
|
+
post.save
|
|
46
|
+
expect(post.reload.title).to eq ({ "publish" => false, "foo" => "bar2" })
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should support IS JSON" do
|
|
50
|
+
TestPost.create!(title: { "publish" => true, "foo" => "bar" })
|
|
51
|
+
count_json = TestPost.where("title is json")
|
|
52
|
+
expect(count_json.size).to eq 1
|
|
53
|
+
count_non_json = TestPost.where("title is not json")
|
|
54
|
+
expect(count_non_json.size).to eq 0
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter quoting of NCHAR and NVARCHAR2 columns" do
|
|
4
|
+
before(:all) do
|
|
5
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
6
|
+
@conn = ActiveRecord::Base.connection
|
|
7
|
+
@conn.execute <<~SQL
|
|
8
|
+
CREATE TABLE test_items (
|
|
9
|
+
id NUMBER(6,0) PRIMARY KEY,
|
|
10
|
+
nchar_column NCHAR(20),
|
|
11
|
+
nvarchar2_column NVARCHAR2(20),
|
|
12
|
+
char_column CHAR(20),
|
|
13
|
+
varchar2_column VARCHAR2(20)
|
|
14
|
+
)
|
|
15
|
+
SQL
|
|
16
|
+
@conn.execute "CREATE SEQUENCE test_items_seq"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
after(:all) do
|
|
20
|
+
@conn.execute "DROP TABLE test_items"
|
|
21
|
+
@conn.execute "DROP SEQUENCE test_items_seq"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
before(:each) do
|
|
25
|
+
class ::TestItem < ActiveRecord::Base
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
after(:each) do
|
|
30
|
+
Object.send(:remove_const, "TestItem")
|
|
31
|
+
ActiveRecord::Base.clear_cache!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should quote with N prefix" do
|
|
35
|
+
columns = @conn.columns("test_items")
|
|
36
|
+
%w(nchar_column nvarchar2_column char_column varchar2_column).each do |col|
|
|
37
|
+
column = columns.detect { |c| c.name == col }
|
|
38
|
+
type = @conn.lookup_cast_type(column.sql_type)
|
|
39
|
+
value = type.serialize("abc")
|
|
40
|
+
expect(@conn.quote(value)).to eq(column.sql_type[0, 1] == "N" ? "N'abc'" : "'abc'")
|
|
41
|
+
nilvalue = type.serialize(nil)
|
|
42
|
+
expect(@conn.quote(nilvalue)).to eq("NULL")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should create record" do
|
|
47
|
+
nchar_data = "āčē"
|
|
48
|
+
item = TestItem.create(
|
|
49
|
+
nchar_column: nchar_data,
|
|
50
|
+
nvarchar2_column: nchar_data
|
|
51
|
+
).reload
|
|
52
|
+
expect(item.nchar_column).to eq(nchar_data)
|
|
53
|
+
expect(item.nvarchar2_column).to eq(nchar_data)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe "OracleEnhancedAdapter handling of NCLOB columns" do
|
|
4
|
+
include SchemaSpecHelper
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
|
|
8
|
+
@conn = ActiveRecord::Base.connection
|
|
9
|
+
schema_define do
|
|
10
|
+
create_table :test_employees, force: true do |t|
|
|
11
|
+
t.string :first_name, limit: 20
|
|
12
|
+
t.string :last_name, limit: 25
|
|
13
|
+
t.ntext :comments
|
|
14
|
+
end
|
|
15
|
+
create_table :test2_employees, force: true do |t|
|
|
16
|
+
t.string :first_name, limit: 20
|
|
17
|
+
t.string :last_name, limit: 25
|
|
18
|
+
t.ntext :comments
|
|
19
|
+
end
|
|
20
|
+
create_table :test_serialize_employees, force: true do |t|
|
|
21
|
+
t.string :first_name, limit: 20
|
|
22
|
+
t.string :last_name, limit: 25
|
|
23
|
+
end
|
|
24
|
+
add_column :test_serialize_employees, :comments, :ntext
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Some random multibyte characters. They say Hello (Kon'nichiwa) World (Sekai) in Japanese.
|
|
28
|
+
@nclob_data = "こんにちは"
|
|
29
|
+
@nclob_data2 = "世界"
|
|
30
|
+
|
|
31
|
+
class ::TestEmployee < ActiveRecord::Base; end
|
|
32
|
+
class ::Test2Employee < ActiveRecord::Base
|
|
33
|
+
serialize :comments
|
|
34
|
+
end
|
|
35
|
+
class ::TestEmployeeReadOnlyNClob < ActiveRecord::Base
|
|
36
|
+
self.table_name = "test_employees"
|
|
37
|
+
attr_readonly :comments
|
|
38
|
+
end
|
|
39
|
+
class ::TestSerializeEmployee < ActiveRecord::Base
|
|
40
|
+
serialize :comments
|
|
41
|
+
attr_readonly :comments
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after(:all) do
|
|
46
|
+
@conn.drop_table :test_employees, if_exists: true
|
|
47
|
+
@conn.drop_table :test2_employees, if_exists: true
|
|
48
|
+
@conn.drop_table :test_serialize_employees, if_exists: true
|
|
49
|
+
Object.send(:remove_const, "TestEmployee")
|
|
50
|
+
Object.send(:remove_const, "Test2Employee")
|
|
51
|
+
Object.send(:remove_const, "TestEmployeeReadOnlyNClob")
|
|
52
|
+
Object.send(:remove_const, "TestSerializeEmployee")
|
|
53
|
+
ActiveRecord::Base.clear_cache!
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should create record without NCLOB data when attribute is serialized" do
|
|
57
|
+
@employee = Test2Employee.create!(
|
|
58
|
+
first_name: "First",
|
|
59
|
+
last_name: "Last"
|
|
60
|
+
)
|
|
61
|
+
expect(@employee).to be_valid
|
|
62
|
+
@employee.reload
|
|
63
|
+
expect(@employee.comments).to be_nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should accept Symbol value for NCLOB column" do
|
|
67
|
+
@employee = TestEmployee.create!(
|
|
68
|
+
comments: :test_comment
|
|
69
|
+
)
|
|
70
|
+
expect(@employee).to be_valid
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should respect attr_readonly setting for NCLOB column" do
|
|
74
|
+
@employee = TestEmployeeReadOnlyNClob.create!(
|
|
75
|
+
first_name: "First",
|
|
76
|
+
comments: @nclob_data
|
|
77
|
+
)
|
|
78
|
+
expect(@employee).to be_valid
|
|
79
|
+
@employee.reload
|
|
80
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
81
|
+
@employee.comments = @nclob_data2
|
|
82
|
+
expect(@employee.save).to be(true)
|
|
83
|
+
@employee.reload
|
|
84
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should work for serialized readonly NCLOB columns", serialized: true do
|
|
88
|
+
@employee = TestSerializeEmployee.new(
|
|
89
|
+
first_name: "First",
|
|
90
|
+
comments: nil
|
|
91
|
+
)
|
|
92
|
+
expect(@employee.comments).to be_nil
|
|
93
|
+
expect(@employee.save).to be(true)
|
|
94
|
+
expect(@employee).to be_valid
|
|
95
|
+
@employee.reload
|
|
96
|
+
expect(@employee.comments).to be_nil
|
|
97
|
+
@employee.comments = {}
|
|
98
|
+
expect(@employee.save).to be(true)
|
|
99
|
+
@employee.reload
|
|
100
|
+
# should not set readonly
|
|
101
|
+
expect(@employee.comments).to be_nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should create record with NCLOB data" do
|
|
105
|
+
@employee = TestEmployee.create!(
|
|
106
|
+
first_name: "First",
|
|
107
|
+
last_name: "Last",
|
|
108
|
+
comments: @nclob_data
|
|
109
|
+
)
|
|
110
|
+
@employee.reload
|
|
111
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should update record with NCLOB data" do
|
|
115
|
+
@employee = TestEmployee.create!(
|
|
116
|
+
first_name: "First",
|
|
117
|
+
last_name: "Last"
|
|
118
|
+
)
|
|
119
|
+
@employee.reload
|
|
120
|
+
expect(@employee.comments).to be_nil
|
|
121
|
+
@employee.comments = @nclob_data
|
|
122
|
+
@employee.save!
|
|
123
|
+
@employee.reload
|
|
124
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should update record with zero-length NCLOB data" do
|
|
128
|
+
@employee = TestEmployee.create!(
|
|
129
|
+
first_name: "First",
|
|
130
|
+
last_name: "Last"
|
|
131
|
+
)
|
|
132
|
+
@employee.reload
|
|
133
|
+
expect(@employee.comments).to be_nil
|
|
134
|
+
@employee.comments = ""
|
|
135
|
+
@employee.save!
|
|
136
|
+
@employee.reload
|
|
137
|
+
expect(@employee.comments).to eq("")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should update record that has existing NCLOB data with different NCLOB data" do
|
|
141
|
+
@employee = TestEmployee.create!(
|
|
142
|
+
first_name: "First",
|
|
143
|
+
last_name: "Last",
|
|
144
|
+
comments: @nclob_data
|
|
145
|
+
)
|
|
146
|
+
@employee.reload
|
|
147
|
+
@employee.comments = @nclob_data2
|
|
148
|
+
@employee.save!
|
|
149
|
+
@employee.reload
|
|
150
|
+
expect(@employee.comments).to eq(@nclob_data2)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "should update record that has existing NCLOB data with nil" do
|
|
154
|
+
@employee = TestEmployee.create!(
|
|
155
|
+
first_name: "First",
|
|
156
|
+
last_name: "Last",
|
|
157
|
+
comments: @nclob_data
|
|
158
|
+
)
|
|
159
|
+
@employee.reload
|
|
160
|
+
@employee.comments = nil
|
|
161
|
+
@employee.save!
|
|
162
|
+
@employee.reload
|
|
163
|
+
expect(@employee.comments).to be_nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should update record that has existing NCLOB data with zero-length NCLOB data" do
|
|
167
|
+
@employee = TestEmployee.create!(
|
|
168
|
+
first_name: "First",
|
|
169
|
+
last_name: "Last",
|
|
170
|
+
comments: @nclob_data
|
|
171
|
+
)
|
|
172
|
+
@employee.reload
|
|
173
|
+
@employee.comments = ""
|
|
174
|
+
@employee.save!
|
|
175
|
+
@employee.reload
|
|
176
|
+
expect(@employee.comments).to eq("")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "should update record that has zero-length NCLOB data with non-empty NCLOB data" do
|
|
180
|
+
@employee = TestEmployee.create!(
|
|
181
|
+
first_name: "First",
|
|
182
|
+
last_name: "Last",
|
|
183
|
+
comments: ""
|
|
184
|
+
)
|
|
185
|
+
@employee.reload
|
|
186
|
+
expect(@employee.comments).to eq("")
|
|
187
|
+
@employee.comments = @nclob_data
|
|
188
|
+
@employee.save!
|
|
189
|
+
@employee.reload
|
|
190
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "should store serializable ruby data structures" do
|
|
194
|
+
ruby_data1 = { "arbitrary1" => ["ruby", :data, 123] }
|
|
195
|
+
ruby_data2 = { "arbitrary2" => ["ruby", :data, 123] }
|
|
196
|
+
@employee = Test2Employee.create!(
|
|
197
|
+
comments: ruby_data1
|
|
198
|
+
)
|
|
199
|
+
@employee.reload
|
|
200
|
+
expect(@employee.comments).to eq(ruby_data1)
|
|
201
|
+
@employee.comments = ruby_data2
|
|
202
|
+
@employee.save
|
|
203
|
+
@employee.reload
|
|
204
|
+
expect(@employee.comments).to eq(ruby_data2)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "should keep unchanged serialized data when other columns changed" do
|
|
208
|
+
@employee = Test2Employee.create!(
|
|
209
|
+
first_name: "First",
|
|
210
|
+
last_name: "Last",
|
|
211
|
+
comments: @nclob_data
|
|
212
|
+
)
|
|
213
|
+
@employee.first_name = "Steve"
|
|
214
|
+
@employee.save
|
|
215
|
+
@employee.reload
|
|
216
|
+
expect(@employee.comments).to eq(@nclob_data)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "should keep serialized data after save" do
|
|
220
|
+
@employee = Test2Employee.new
|
|
221
|
+
@employee.comments = { length: { is: 1 } }
|
|
222
|
+
@employee.save
|
|
223
|
+
@employee.reload
|
|
224
|
+
expect(@employee.comments).to eq(length: { is: 1 })
|
|
225
|
+
@employee.comments = { length: { is: 2 } }
|
|
226
|
+
@employee.save
|
|
227
|
+
@employee.reload
|
|
228
|
+
expect(@employee.comments).to eq(length: { is: 2 })
|
|
229
|
+
end
|
|
230
|
+
end
|