activerecord-jdbc-adapter 1.3.17 → 1.3.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +24 -5
- data/History.md +54 -0
- data/lib/arel/visitors/compat.rb +30 -2
- data/lib/arel/visitors/db2.rb +118 -29
- data/lib/arel/visitors/derby.rb +84 -29
- data/lib/arel/visitors/firebird.rb +66 -9
- data/lib/arel/visitors/h2.rb +16 -0
- data/lib/arel/visitors/hsqldb.rb +6 -3
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +121 -40
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +1 -7
- data/lib/arjdbc/db2.rb +1 -0
- data/lib/arjdbc/db2/adapter.rb +118 -18
- data/lib/arjdbc/derby/adapter.rb +29 -8
- data/lib/arjdbc/firebird.rb +1 -0
- data/lib/arjdbc/firebird/adapter.rb +126 -11
- data/lib/arjdbc/hsqldb/adapter.rb +3 -0
- data/lib/arjdbc/informix.rb +1 -0
- data/lib/arjdbc/jdbc.rb +17 -0
- data/lib/arjdbc/jdbc/adapter.rb +28 -3
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +7 -3
- data/lib/arjdbc/jdbc/type_cast.rb +2 -0
- data/lib/arjdbc/jdbc/type_converter.rb +28 -15
- data/lib/arjdbc/mimer.rb +1 -0
- data/lib/arjdbc/mssql.rb +2 -1
- data/lib/arjdbc/mssql/adapter.rb +105 -30
- data/lib/arjdbc/mssql/column.rb +30 -7
- data/lib/arjdbc/mssql/limit_helpers.rb +22 -9
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +25 -2
- data/lib/arjdbc/mysql/adapter.rb +22 -21
- data/lib/arjdbc/oracle.rb +1 -0
- data/lib/arjdbc/oracle/adapter.rb +291 -19
- data/lib/arjdbc/oracle/column.rb +9 -5
- data/lib/arjdbc/oracle/connection_methods.rb +4 -1
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +7 -1
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +2 -1
- data/lib/arjdbc/tasks/database_tasks.rb +3 -0
- data/lib/arjdbc/util/quoted_cache.rb +2 -2
- data/lib/arjdbc/util/serialized_attributes.rb +11 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +1 -1
- data/rakelib/db.rake +3 -1
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +259 -61
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +13 -2
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +192 -15
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +10 -2
- metadata +9 -4
data/lib/arjdbc/mssql/utils.rb
CHANGED
@@ -1,12 +1,35 @@
|
|
1
|
+
# NOTE: file contains code adapted from **sqlserver** adapter, license follows
|
2
|
+
=begin
|
3
|
+
Copyright (c) 2008-2015
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
=end
|
24
|
+
|
1
25
|
module ArJdbc
|
2
26
|
module MSSQL
|
3
|
-
# @note Lot of code kindly borrowed from **activerecord-sqlserver-adapter**.
|
4
27
|
module Utils
|
5
28
|
|
6
29
|
module_function
|
7
30
|
|
8
31
|
GET_TABLE_NAME_INSERT_UPDATE_RE =
|
9
|
-
/^\s*(INSERT|EXEC sp_executesql N'INSERT)
|
32
|
+
/^\s*(INSERT|EXEC sp_executesql N'INSERT)(?:\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
10
33
|
|
11
34
|
GET_TABLE_NAME_FROM_RE = /\bFROM\s+([^\(\)\s,]+)\s*/i
|
12
35
|
|
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -553,6 +553,7 @@ module ArJdbc
|
|
553
553
|
# @private
|
554
554
|
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
|
555
555
|
|
556
|
+
# @override
|
556
557
|
def supports_foreign_keys?; true end
|
557
558
|
|
558
559
|
def foreign_keys(table_name)
|
@@ -786,22 +787,22 @@ module ArJdbc
|
|
786
787
|
|
787
788
|
register_class_with_limit m, %r(char)i, MysqlString
|
788
789
|
|
789
|
-
m.register_type %r(tinytext)i, Type::Text.new(limit
|
790
|
-
m.register_type %r(tinyblob)i, Type::Binary.new(limit
|
791
|
-
m.register_type %r(text)i, Type::Text.new(limit
|
792
|
-
m.register_type %r(blob)i, Type::Binary.new(limit
|
793
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit
|
794
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit
|
795
|
-
m.register_type %r(longtext)i, Type::Text.new(limit
|
796
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit
|
797
|
-
m.register_type %r(^float)i, Type::Float.new(limit
|
798
|
-
m.register_type %r(^double)i, Type::Float.new(limit
|
799
|
-
|
800
|
-
register_integer_type m, %r(^bigint)i, limit
|
801
|
-
register_integer_type m, %r(^int)i, limit
|
802
|
-
register_integer_type m, %r(^mediumint)i, limit
|
803
|
-
register_integer_type m, %r(^smallint)i, limit
|
804
|
-
register_integer_type m, %r(^tinyint)i, limit
|
790
|
+
m.register_type %r(tinytext)i, Type::Text.new(:limit => 2**8 - 1)
|
791
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(:limit => 2**8 - 1)
|
792
|
+
m.register_type %r(text)i, Type::Text.new(:limit => 2**16 - 1)
|
793
|
+
m.register_type %r(blob)i, Type::Binary.new(:limit => 2**16 - 1)
|
794
|
+
m.register_type %r(mediumtext)i, Type::Text.new(:limit => 2**24 - 1)
|
795
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(:limit => 2**24 - 1)
|
796
|
+
m.register_type %r(longtext)i, Type::Text.new(:limit => 2**32 - 1)
|
797
|
+
m.register_type %r(longblob)i, Type::Binary.new(:limit => 2**32 - 1)
|
798
|
+
m.register_type %r(^float)i, Type::Float.new(:limit => 24)
|
799
|
+
m.register_type %r(^double)i, Type::Float.new(:limit => 53)
|
800
|
+
|
801
|
+
register_integer_type m, %r(^bigint)i, :limit => 8
|
802
|
+
register_integer_type m, %r(^int)i, :limit => 4
|
803
|
+
register_integer_type m, %r(^mediumint)i, :limit => 3
|
804
|
+
register_integer_type m, %r(^smallint)i, :limit => 2
|
805
|
+
register_integer_type m, %r(^tinyint)i, :limit => 1
|
805
806
|
|
806
807
|
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
|
807
808
|
m.alias_type %r(set)i, 'varchar'
|
@@ -810,13 +811,13 @@ module ArJdbc
|
|
810
811
|
|
811
812
|
m.register_type(%r(datetime)i) do |sql_type|
|
812
813
|
precision = extract_precision(sql_type)
|
813
|
-
MysqlDateTime.new(precision
|
814
|
+
MysqlDateTime.new(:precision => precision)
|
814
815
|
end
|
815
816
|
|
816
817
|
m.register_type(%r(enum)i) do |sql_type|
|
817
|
-
limit = sql_type[/^enum\((.+)\)/i, 1]
|
818
|
-
|
819
|
-
MysqlString.new(limit
|
818
|
+
limit = sql_type[/^enum\((.+)\)/i, 1].split(',').
|
819
|
+
map{|enum| enum.strip.length - 2}.max
|
820
|
+
MysqlString.new(:limit => limit)
|
820
821
|
end
|
821
822
|
end if AR42
|
822
823
|
|
@@ -890,7 +891,7 @@ module ArJdbc
|
|
890
891
|
version << jdbc_connection.serverMinorVersion
|
891
892
|
version << jdbc_connection.serverSubMinorVersion
|
892
893
|
else
|
893
|
-
if match = full_version.match(/^(\d)\.(\d+)\.(\d+)/)
|
894
|
+
if match = full_version.match(/^(\d+)\.(\d+)\.(\d+)/)
|
894
895
|
version << match[1].to_i
|
895
896
|
version << match[2].to_i
|
896
897
|
version << match[3].to_i
|
data/lib/arjdbc/oracle.rb
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
# NOTE: file contains code adapted from **oracle-enhanced** adapter, license follows
|
2
|
+
=begin
|
3
|
+
Copyright (c) 2008-2011 Graham Jenkins, Michael Schoen, Raimonds Simanovskis
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
=end
|
24
|
+
|
1
25
|
ArJdbc.load_java_part :Oracle
|
2
26
|
|
3
27
|
module ArJdbc
|
@@ -70,18 +94,35 @@ module ArJdbc
|
|
70
94
|
def raw(*args)
|
71
95
|
options = args.extract_options!
|
72
96
|
column(args[0], 'raw', options)
|
73
|
-
end
|
97
|
+
end unless AR42
|
98
|
+
|
99
|
+
def raw(name, options={})
|
100
|
+
column(name, :raw, options)
|
101
|
+
end if AR42
|
74
102
|
|
75
103
|
def xml(*args)
|
76
104
|
options = args.extract_options!
|
77
105
|
column(args[0], 'xml', options)
|
78
|
-
end
|
106
|
+
end unless AR42
|
107
|
+
|
108
|
+
def raw(name, options={})
|
109
|
+
column(name, :xml, options)
|
110
|
+
end if AR42
|
111
|
+
|
112
|
+
def aliased_types(name, fallback)
|
113
|
+
# NOTE: disable aliasing :timestamp as :datetime :
|
114
|
+
fallback # 'timestamp' == name ? :datetime : fallback
|
115
|
+
end if AR42
|
79
116
|
end
|
80
117
|
|
81
118
|
def table_definition(*args)
|
82
119
|
new_table_definition(TableDefinition, *args)
|
83
120
|
end
|
84
121
|
|
122
|
+
def create_table_definition(name, temporary, options, as = nil)
|
123
|
+
TableDefinition.new native_database_types, name, temporary, options, as
|
124
|
+
end if AR42
|
125
|
+
|
85
126
|
def self.arel_visitor_type(config = nil)
|
86
127
|
::Arel::Visitors::Oracle
|
87
128
|
end
|
@@ -98,6 +139,63 @@ module ArJdbc
|
|
98
139
|
ADAPTER_NAME
|
99
140
|
end
|
100
141
|
|
142
|
+
def initialize_type_map(m)
|
143
|
+
super
|
144
|
+
|
145
|
+
m.register_type(%r(NUMBER)i) do |sql_type|
|
146
|
+
scale = extract_scale(sql_type)
|
147
|
+
precision = extract_precision(sql_type)
|
148
|
+
limit = extract_limit(sql_type)
|
149
|
+
if scale == 0
|
150
|
+
if Oracle.emulate_booleans? && limit == 1
|
151
|
+
ActiveRecord::Type::Boolean.new
|
152
|
+
else
|
153
|
+
ActiveRecord::Type::Integer.new(:precision => precision, :limit => limit)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
register_class_with_limit m, %r(date)i, ActiveRecord::Type::DateTime
|
161
|
+
register_class_with_limit m, %r(raw)i, RawType
|
162
|
+
register_class_with_limit m, %r(timestamp)i, TimestampType
|
163
|
+
|
164
|
+
m.register_type %r(xmltype)i, XmlType.new
|
165
|
+
end if AR42
|
166
|
+
|
167
|
+
def clear_cache!
|
168
|
+
super
|
169
|
+
reload_type_map
|
170
|
+
end if AR42
|
171
|
+
|
172
|
+
# @private
|
173
|
+
class RawType < ActiveRecord::Type::String
|
174
|
+
def type; :raw end
|
175
|
+
end if AR42
|
176
|
+
|
177
|
+
# @private
|
178
|
+
class TimestampType < ActiveRecord::Type::DateTime
|
179
|
+
def type; :timestamp end
|
180
|
+
end if AR42
|
181
|
+
|
182
|
+
# @private
|
183
|
+
class XmlType < ActiveRecord::Type::String
|
184
|
+
def type; :xml end
|
185
|
+
|
186
|
+
def type_cast_for_database(value)
|
187
|
+
return unless value
|
188
|
+
Data.new(super)
|
189
|
+
end
|
190
|
+
|
191
|
+
class Data
|
192
|
+
def initialize(value)
|
193
|
+
@value = value
|
194
|
+
end
|
195
|
+
def to_s; @value end
|
196
|
+
end
|
197
|
+
end if AR42
|
198
|
+
|
101
199
|
NATIVE_DATABASE_TYPES = {
|
102
200
|
:primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
|
103
201
|
:string => { :name => "VARCHAR2", :limit => 255 },
|
@@ -142,9 +240,16 @@ module ArJdbc
|
|
142
240
|
def column_name_length; IDENTIFIER_LENGTH; end
|
143
241
|
def sequence_name_length; IDENTIFIER_LENGTH end
|
144
242
|
|
145
|
-
|
146
|
-
|
147
|
-
|
243
|
+
# @private
|
244
|
+
# Will take all or first 26 characters of table name and append _seq suffix
|
245
|
+
def default_sequence_name(table_name, primary_key = nil)
|
246
|
+
len = IDENTIFIER_LENGTH - 4
|
247
|
+
table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
|
248
|
+
end
|
249
|
+
|
250
|
+
# @private
|
251
|
+
def default_trigger_name(table_name)
|
252
|
+
"#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
|
148
253
|
end
|
149
254
|
|
150
255
|
# @override
|
@@ -177,7 +282,7 @@ module ArJdbc
|
|
177
282
|
seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
|
178
283
|
options[:sequence_name] : default_sequence_name(name)
|
179
284
|
return outcome unless seq_name
|
180
|
-
|
285
|
+
execute_quietly "DROP SEQUENCE #{quote_table_name(seq_name)}"
|
181
286
|
end
|
182
287
|
|
183
288
|
# @override
|
@@ -206,7 +311,7 @@ module ArJdbc
|
|
206
311
|
end
|
207
312
|
|
208
313
|
def indexes(table, name = nil)
|
209
|
-
@connection.indexes(table, name,
|
314
|
+
@connection.indexes(table, name, schema_owner)
|
210
315
|
end
|
211
316
|
|
212
317
|
# @note Only used with (non-AREL) ActiveRecord **2.3**.
|
@@ -245,13 +350,66 @@ module ArJdbc
|
|
245
350
|
# no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
|
246
351
|
end
|
247
352
|
|
353
|
+
# @override
|
354
|
+
def add_index(table_name, column_name, options = {})
|
355
|
+
index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
|
356
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
|
357
|
+
if index_type == 'UNIQUE'
|
358
|
+
unless quoted_column_names =~ /\(.*\)/
|
359
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end if AR42
|
363
|
+
|
364
|
+
# @private
|
365
|
+
def add_index_options(table_name, column_name, options = {})
|
366
|
+
column_names = Array(column_name)
|
367
|
+
index_name = index_name(table_name, column: column_names)
|
368
|
+
|
369
|
+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
|
370
|
+
|
371
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
372
|
+
index_name = options[:name].to_s if options.key?(:name)
|
373
|
+
tablespace = '' # tablespace_for(:index, options[:tablespace])
|
374
|
+
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
375
|
+
index_options = '' # index_options = options[:options]
|
376
|
+
|
377
|
+
if index_name.to_s.length > max_index_length
|
378
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
|
379
|
+
end
|
380
|
+
if index_name_exists?(table_name, index_name, false)
|
381
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
382
|
+
end
|
383
|
+
|
384
|
+
quoted_column_names = column_names.map { |e| quote_column_name(e, true) }.join(", ")
|
385
|
+
[ index_name, index_type, quoted_column_names, tablespace, index_options ]
|
386
|
+
end if AR42
|
387
|
+
|
388
|
+
# @override
|
389
|
+
def remove_index(table_name, options = {})
|
390
|
+
index_name = index_name(table_name, options)
|
391
|
+
unless index_name_exists?(table_name, index_name, true)
|
392
|
+
# sometimes options can be String or Array with column names
|
393
|
+
options = {} unless options.is_a?(Hash)
|
394
|
+
if options.has_key? :name
|
395
|
+
options_without_column = options.dup
|
396
|
+
options_without_column.delete :column
|
397
|
+
index_name_without_column = index_name(table_name, options_without_column)
|
398
|
+
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
399
|
+
end
|
400
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
401
|
+
end
|
402
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
|
403
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
404
|
+
end if AR42
|
405
|
+
|
406
|
+
# @private
|
248
407
|
def remove_index(table_name, options = {})
|
249
408
|
execute "DROP INDEX #{index_name(table_name, options)}"
|
250
|
-
end
|
409
|
+
end unless AR42
|
251
410
|
|
252
411
|
def change_column_default(table_name, column_name, default)
|
253
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} "
|
254
|
-
"MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
412
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
255
413
|
end
|
256
414
|
|
257
415
|
# @override
|
@@ -396,17 +554,24 @@ module ArJdbc
|
|
396
554
|
name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
|
397
555
|
end
|
398
556
|
|
557
|
+
# @private
|
558
|
+
LOWER_CASE_ONLY = /\A[a-z][a-z_0-9\$#]*\Z/
|
559
|
+
|
399
560
|
# @override
|
400
|
-
def quote_column_name(name)
|
561
|
+
def quote_column_name(name, handle_expression = false)
|
401
562
|
# if only valid lowercase column characters in name
|
402
|
-
if ( name = name.to_s ) =~
|
563
|
+
if ( name = name.to_s ) =~ LOWER_CASE_ONLY
|
403
564
|
# putting double-quotes around an identifier causes Oracle to treat the
|
404
565
|
# identifier as case sensitive (otherwise assumes case-insensitivity) !
|
405
566
|
# all upper case is an exception, where double-quotes are meaningless
|
406
567
|
"\"#{name.upcase}\"" # name.upcase
|
407
568
|
else
|
408
|
-
|
409
|
-
|
569
|
+
if handle_expression
|
570
|
+
name =~ /^[a-z][a-z_0-9\$#\-]*$/i ? "\"#{name}\"" : name
|
571
|
+
else
|
572
|
+
# remove double quotes which cannot be used inside quoted identifier
|
573
|
+
"\"#{name.gsub('"', '')}\""
|
574
|
+
end
|
410
575
|
end
|
411
576
|
end
|
412
577
|
|
@@ -516,23 +681,110 @@ module ArJdbc
|
|
516
681
|
result
|
517
682
|
end
|
518
683
|
|
684
|
+
@@do_not_prefetch_primary_key = {}
|
685
|
+
|
519
686
|
# Returns true for Oracle adapter (since Oracle requires primary key
|
520
687
|
# values to be pre-fetched before insert).
|
521
688
|
# @see #next_sequence_value
|
522
689
|
# @override
|
523
690
|
def prefetch_primary_key?(table_name = nil)
|
524
691
|
return true if table_name.nil?
|
525
|
-
|
526
|
-
|
692
|
+
do_not_prefetch_hash = @@do_not_prefetch_primary_key
|
693
|
+
do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
|
694
|
+
if do_not_prefetch.nil?
|
695
|
+
owner, desc_table_name, db_link = describe(table_name)
|
696
|
+
do_not_prefetch_hash[table_name] = do_not_prefetch =
|
697
|
+
! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
|
698
|
+
has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
699
|
+
end
|
700
|
+
! do_not_prefetch
|
527
701
|
end
|
528
702
|
|
529
|
-
#
|
703
|
+
# used to clear prefetch primary key flag for all tables
|
704
|
+
# @private
|
705
|
+
def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
|
706
|
+
|
707
|
+
# @private
|
708
|
+
def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
709
|
+
! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
710
|
+
end
|
711
|
+
|
712
|
+
# @private check if table has primary key trigger with _pkt suffix
|
713
|
+
def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
714
|
+
(owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
|
715
|
+
|
716
|
+
trigger_name = default_trigger_name(table_name).upcase
|
717
|
+
pkt_sql = "SELECT trigger_name FROM all_triggers#{db_link} WHERE owner = '#{owner}'" <<
|
718
|
+
" AND trigger_name = '#{trigger_name}'" <<
|
719
|
+
" AND table_owner = '#{owner}'" <<
|
720
|
+
" AND table_name = '#{desc_table_name}'" <<
|
721
|
+
" AND status = 'ENABLED'"
|
722
|
+
select_value(pkt_sql, 'Primary Key Trigger') ? true : false
|
723
|
+
end
|
724
|
+
|
725
|
+
# use in set_sequence_name to avoid fetching primary key value from sequence
|
726
|
+
AUTOGENERATED_SEQUENCE_NAME = 'autogenerated'.freeze
|
727
|
+
|
728
|
+
# Returns the next sequence value from a sequence generator. Not generally
|
729
|
+
# called directly; used by ActiveRecord to get the next primary key value
|
730
|
+
# when inserting a new database record (see #prefetch_primary_key?).
|
530
731
|
def next_sequence_value(sequence_name)
|
732
|
+
# if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
|
733
|
+
return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
|
531
734
|
sequence_name = quote_table_name(sequence_name)
|
532
735
|
sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
|
533
736
|
log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
|
534
737
|
end
|
535
738
|
|
739
|
+
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
740
|
+
(owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
|
741
|
+
|
742
|
+
seqs = "SELECT us.sequence_name" <<
|
743
|
+
" FROM all_sequences#{db_link} us" <<
|
744
|
+
" WHERE us.sequence_owner = '#{owner}'" <<
|
745
|
+
" AND us.sequence_name = '#{desc_table_name}_SEQ'"
|
746
|
+
seqs = select_values(seqs, 'Sequence')
|
747
|
+
|
748
|
+
# changed back from user_constraints to all_constraints for consistency
|
749
|
+
pks = "SELECT cc.column_name" <<
|
750
|
+
" FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc" <<
|
751
|
+
" WHERE c.owner = '#{owner}'" <<
|
752
|
+
" AND c.table_name = '#{desc_table_name}'" <<
|
753
|
+
" AND c.constraint_type = 'P'" <<
|
754
|
+
" AND cc.owner = c.owner" <<
|
755
|
+
" AND cc.constraint_name = c.constraint_name"
|
756
|
+
pks = select_values(pks, 'Primary Key')
|
757
|
+
|
758
|
+
# only support single column keys
|
759
|
+
pks.size == 1 ? [oracle_downcase(pks.first), oracle_downcase(seqs.first)] : nil
|
760
|
+
end
|
761
|
+
private :pk_and_sequence_for
|
762
|
+
|
763
|
+
# Returns just a table's primary key
|
764
|
+
def primary_key(table_name)
|
765
|
+
pk_and_sequence = pk_and_sequence_for(table_name)
|
766
|
+
pk_and_sequence && pk_and_sequence.first
|
767
|
+
end
|
768
|
+
|
769
|
+
# @override
|
770
|
+
def supports_foreign_keys?; true end
|
771
|
+
|
772
|
+
# @private
|
773
|
+
def disable_referential_integrity
|
774
|
+
sql_constraints = "SELECT constraint_name, owner, table_name FROM user_constraints WHERE constraint_type = 'R' AND status = 'ENABLED'"
|
775
|
+
old_constraints = select_all(sql_constraints)
|
776
|
+
begin
|
777
|
+
old_constraints.each do |constraint|
|
778
|
+
execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
|
779
|
+
end
|
780
|
+
yield
|
781
|
+
ensure
|
782
|
+
old_constraints.reverse_each do |constraint|
|
783
|
+
execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
536
788
|
# @override (for AR <= 3.0)
|
537
789
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
538
790
|
# if PK is already pre-fetched from sequence or if there is no PK :
|
@@ -647,6 +899,26 @@ module ArJdbc
|
|
647
899
|
end
|
648
900
|
end
|
649
901
|
|
902
|
+
# default schema owner
|
903
|
+
def schema_owner(force = true)
|
904
|
+
unless defined? @schema_owner
|
905
|
+
username = config[:username] ? config[:username].to_s : nil
|
906
|
+
username = jdbc_connection.meta_data.user_name if force && username.nil?
|
907
|
+
@schema_owner = username.nil? ? nil : username.upcase
|
908
|
+
end
|
909
|
+
@schema_owner
|
910
|
+
end
|
911
|
+
|
912
|
+
# do not force reading schema_owner as we're read on our own ...
|
913
|
+
def describe(table_name, owner = schema_owner(false))
|
914
|
+
@connection.describe(table_name, owner)
|
915
|
+
end
|
916
|
+
|
917
|
+
def oracle_downcase(column_name)
|
918
|
+
return nil if column_name.nil?
|
919
|
+
column_name =~ /[a-z]/ ? column_name : column_name.downcase
|
920
|
+
end
|
921
|
+
|
650
922
|
end
|
651
923
|
end
|
652
924
|
|
@@ -660,8 +932,8 @@ module ActiveRecord::ConnectionAdapters
|
|
660
932
|
include ::ArJdbc::Oracle
|
661
933
|
include ::ArJdbc::Util::QuotedCache
|
662
934
|
|
663
|
-
# By default, the
|
664
|
-
# <tt>
|
935
|
+
# By default, the OracleAdapter will consider all columns of type
|
936
|
+
# <tt>NUMBER(1)</tt> as boolean. If you wish to disable this :
|
665
937
|
#
|
666
938
|
# ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
|
667
939
|
#
|