activerecord-jdbc-adapter 60.3-java → 61.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 +4 -4
- data/.travis.yml +11 -11
- data/Gemfile +1 -1
- data/README.md +8 -7
- data/activerecord-jdbc-adapter.gemspec +1 -1
- data/lib/arel/visitors/postgresql_jdbc.rb +1 -1
- data/lib/arjdbc/abstract/core.rb +1 -0
- data/lib/arjdbc/abstract/database_statements.rb +4 -0
- data/lib/arjdbc/abstract/transaction_support.rb +20 -7
- data/lib/arjdbc/mysql/adapter.rb +14 -5
- data/lib/arjdbc/mysql/connection_methods.rb +5 -1
- data/lib/arjdbc/postgresql/adapter.rb +85 -73
- data/lib/arjdbc/postgresql/column.rb +1 -1
- data/lib/arjdbc/postgresql/oid_types.rb +4 -3
- data/lib/arjdbc/sqlite3/adapter.rb +95 -58
- data/lib/arjdbc/sqlite3/connection_methods.rb +11 -1
- data/lib/arjdbc/tasks/databases.rake +15 -10
- data/lib/arjdbc/version.rb +1 -1
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +106 -68
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +81 -22
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +3 -4
- metadata +3 -3
@@ -48,7 +48,7 @@ module ArJdbc
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def has_default_function?(default_value, default)
|
51
|
-
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
51
|
+
!default_value && default && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -145,7 +145,7 @@ module ArJdbc
|
|
145
145
|
m.register_type 'uuid', OID::Uuid.new
|
146
146
|
m.register_type 'xml', OID::Xml.new
|
147
147
|
m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
|
148
|
-
m.register_type 'macaddr', OID::
|
148
|
+
m.register_type 'macaddr', OID::Macaddr.new
|
149
149
|
m.register_type 'citext', OID::SpecializedString.new(:citext)
|
150
150
|
m.register_type 'ltree', OID::SpecializedString.new(:ltree)
|
151
151
|
m.register_type 'line', OID::SpecializedString.new(:line)
|
@@ -155,9 +155,9 @@ module ArJdbc
|
|
155
155
|
m.register_type 'polygon', OID::SpecializedString.new(:polygon)
|
156
156
|
m.register_type 'circle', OID::SpecializedString.new(:circle)
|
157
157
|
|
158
|
-
m.register_type 'interval' do
|
158
|
+
m.register_type 'interval' do |*args, sql_type|
|
159
159
|
precision = extract_precision(sql_type)
|
160
|
-
OID::
|
160
|
+
OID::Interval.new(precision: precision)
|
161
161
|
end
|
162
162
|
|
163
163
|
register_class_with_precision m, 'time', Type::Time
|
@@ -244,6 +244,7 @@ module ArJdbc
|
|
244
244
|
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
245
245
|
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
246
246
|
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
247
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
247
248
|
ActiveRecord::Type.register(:json, Type::Json, adapter: :postgresql)
|
248
249
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
249
250
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
@@ -16,6 +16,33 @@ require "active_record/connection_adapters/sqlite3/schema_dumper"
|
|
16
16
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
17
17
|
require "active_support/core_ext/class/attribute"
|
18
18
|
|
19
|
+
module SQLite3
|
20
|
+
module Constants
|
21
|
+
module Open
|
22
|
+
READONLY = 0x00000001
|
23
|
+
READWRITE = 0x00000002
|
24
|
+
CREATE = 0x00000004
|
25
|
+
DELETEONCLOSE = 0x00000008
|
26
|
+
EXCLUSIVE = 0x00000010
|
27
|
+
AUTOPROXY = 0x00000020
|
28
|
+
URI = 0x00000040
|
29
|
+
MEMORY = 0x00000080
|
30
|
+
MAIN_DB = 0x00000100
|
31
|
+
TEMP_DB = 0x00000200
|
32
|
+
TRANSIENT_DB = 0x00000400
|
33
|
+
MAIN_JOURNAL = 0x00000800
|
34
|
+
TEMP_JOURNAL = 0x00001000
|
35
|
+
SUBJOURNAL = 0x00002000
|
36
|
+
MASTER_JOURNAL = 0x00004000
|
37
|
+
NOMUTEX = 0x00008000
|
38
|
+
FULLMUTEX = 0x00010000
|
39
|
+
SHAREDCACHE = 0x00020000
|
40
|
+
PRIVATECACHE = 0x00040000
|
41
|
+
WAL = 0x00080000
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
19
46
|
module ArJdbc
|
20
47
|
# All the code in this module is a copy of ConnectionAdapters::SQLite3Adapter from active_record 5.
|
21
48
|
# The constants at the front of this file are to allow the rest of the file to remain with no modifications
|
@@ -69,6 +96,10 @@ module ArJdbc
|
|
69
96
|
true
|
70
97
|
end
|
71
98
|
|
99
|
+
def supports_transaction_isolation?
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
72
103
|
def supports_partial_index?
|
73
104
|
database_version >= "3.9.0"
|
74
105
|
end
|
@@ -85,6 +116,10 @@ module ArJdbc
|
|
85
116
|
true
|
86
117
|
end
|
87
118
|
|
119
|
+
def supports_check_constraints?
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
88
123
|
def supports_views?
|
89
124
|
true
|
90
125
|
end
|
@@ -114,13 +149,6 @@ module ArJdbc
|
|
114
149
|
true
|
115
150
|
end
|
116
151
|
|
117
|
-
# Returns 62. SQLite supports index names up to 64
|
118
|
-
# characters. The rest is used by Rails internally to perform
|
119
|
-
# temporary rename operations
|
120
|
-
def allowed_index_name_length
|
121
|
-
index_name_length - 2
|
122
|
-
end
|
123
|
-
|
124
152
|
def native_database_types #:nodoc:
|
125
153
|
NATIVE_DATABASE_TYPES
|
126
154
|
end
|
@@ -159,7 +187,7 @@ module ArJdbc
|
|
159
187
|
#++
|
160
188
|
|
161
189
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
162
|
-
:
|
190
|
+
:pragma
|
163
191
|
) # :nodoc:
|
164
192
|
private_constant :READ_QUERY
|
165
193
|
|
@@ -187,15 +215,15 @@ module ArJdbc
|
|
187
215
|
#def execute(sql, name = nil) #:nodoc:
|
188
216
|
|
189
217
|
def begin_db_transaction #:nodoc:
|
190
|
-
log("begin transaction",
|
218
|
+
log("begin transaction", 'TRANSACTION') { @connection.transaction }
|
191
219
|
end
|
192
220
|
|
193
221
|
def commit_db_transaction #:nodoc:
|
194
|
-
log("commit transaction",
|
222
|
+
log("commit transaction", 'TRANSACTION') { @connection.commit }
|
195
223
|
end
|
196
224
|
|
197
225
|
def exec_rollback_db_transaction #:nodoc:
|
198
|
-
log("rollback transaction",
|
226
|
+
log("rollback transaction", 'TRANSACTION') { @connection.rollback }
|
199
227
|
end
|
200
228
|
|
201
229
|
# SCHEMA STATEMENTS ========================================
|
@@ -205,8 +233,11 @@ module ArJdbc
|
|
205
233
|
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
206
234
|
end
|
207
235
|
|
208
|
-
|
209
|
-
|
236
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
237
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
238
|
+
|
239
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
240
|
+
|
210
241
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
211
242
|
end
|
212
243
|
|
@@ -215,14 +246,16 @@ module ArJdbc
|
|
215
246
|
# Example:
|
216
247
|
# rename_table('octopuses', 'octopi')
|
217
248
|
def rename_table(table_name, new_name)
|
249
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
250
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
218
251
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
219
252
|
rename_table_indexes(table_name, new_name)
|
220
253
|
end
|
221
254
|
|
222
|
-
def add_column(table_name, column_name, type, options
|
255
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
223
256
|
if invalid_alter_table_type?(type, options)
|
224
257
|
alter_table(table_name) do |definition|
|
225
|
-
definition.column(column_name, type, options)
|
258
|
+
definition.column(column_name, type, **options)
|
226
259
|
end
|
227
260
|
else
|
228
261
|
super
|
@@ -255,16 +288,11 @@ module ArJdbc
|
|
255
288
|
end
|
256
289
|
end
|
257
290
|
|
258
|
-
def change_column(table_name, column_name, type, options
|
291
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
259
292
|
alter_table(table_name) do |definition|
|
260
293
|
definition[column_name].instance_eval do
|
261
294
|
self.type = type
|
262
|
-
|
263
|
-
self.default = options[:default] if options.include?(:default)
|
264
|
-
self.null = options[:null] if options.include?(:null)
|
265
|
-
self.precision = options[:precision] if options.include?(:precision)
|
266
|
-
self.scale = options[:scale] if options.include?(:scale)
|
267
|
-
self.collation = options[:collation] if options.include?(:collation)
|
295
|
+
self.options.merge!(options)
|
268
296
|
end
|
269
297
|
end
|
270
298
|
end
|
@@ -313,12 +341,17 @@ module ArJdbc
|
|
313
341
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
314
342
|
elsif insert.update_duplicates?
|
315
343
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
344
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
316
345
|
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
317
346
|
end
|
318
347
|
|
319
348
|
sql
|
320
349
|
end
|
321
350
|
|
351
|
+
def shared_cache?
|
352
|
+
config[:properties] && config[:properties][:shared_cache] == true
|
353
|
+
end
|
354
|
+
|
322
355
|
def get_database_version # :nodoc:
|
323
356
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
324
357
|
end
|
@@ -327,15 +360,6 @@ module ArJdbc
|
|
327
360
|
"DELETE FROM #{quote_table_name(table_name)}"
|
328
361
|
end
|
329
362
|
|
330
|
-
def build_truncate_statements(*table_names)
|
331
|
-
table_names.flatten.map { |table_name| build_truncate_statement table_name }
|
332
|
-
end
|
333
|
-
|
334
|
-
def truncate(table_name, name = nil)
|
335
|
-
ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
|
336
|
-
execute(build_truncate_statement(table_name), name)
|
337
|
-
end
|
338
|
-
|
339
363
|
def check_version
|
340
364
|
if database_version < "3.8.0"
|
341
365
|
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
@@ -349,11 +373,6 @@ module ArJdbc
|
|
349
373
|
999
|
350
374
|
end
|
351
375
|
|
352
|
-
def initialize_type_map(m = type_map)
|
353
|
-
super
|
354
|
-
register_class_with_limit m, %r(int)i, SQLite3Integer
|
355
|
-
end
|
356
|
-
|
357
376
|
def table_structure(table_name)
|
358
377
|
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
359
378
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
@@ -368,7 +387,12 @@ module ArJdbc
|
|
368
387
|
options[:null] == false && options[:default].nil?
|
369
388
|
end
|
370
389
|
|
371
|
-
def alter_table(
|
390
|
+
def alter_table(
|
391
|
+
table_name,
|
392
|
+
foreign_keys = foreign_keys(table_name),
|
393
|
+
check_constraints = check_constraints(table_name),
|
394
|
+
**options
|
395
|
+
)
|
372
396
|
altered_table_name = "a#{table_name}"
|
373
397
|
|
374
398
|
caller = lambda do |definition|
|
@@ -378,7 +402,11 @@ module ArJdbc
|
|
378
402
|
fk.options[:column] = column
|
379
403
|
end
|
380
404
|
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
381
|
-
definition.foreign_key(to_table, fk.options)
|
405
|
+
definition.foreign_key(to_table, **fk.options)
|
406
|
+
end
|
407
|
+
|
408
|
+
check_constraints.each do |chk|
|
409
|
+
definition.check_constraint(chk.expression, **chk.options)
|
382
410
|
end
|
383
411
|
|
384
412
|
yield definition if block_given?
|
@@ -400,11 +428,12 @@ module ArJdbc
|
|
400
428
|
def copy_table(from, to, options = {})
|
401
429
|
from_primary_key = primary_key(from)
|
402
430
|
options[:id] = false
|
403
|
-
create_table(to, options) do |definition|
|
431
|
+
create_table(to, **options) do |definition|
|
404
432
|
@definition = definition
|
405
433
|
if from_primary_key.is_a?(Array)
|
406
434
|
@definition.primary_keys from_primary_key
|
407
435
|
end
|
436
|
+
|
408
437
|
columns(from).each do |column|
|
409
438
|
column_name = options[:rename] ?
|
410
439
|
(options[:rename][column.name] ||
|
@@ -432,7 +461,7 @@ module ArJdbc
|
|
432
461
|
name = index.name
|
433
462
|
# indexes sqlite creates for internal use start with `sqlite_` and
|
434
463
|
# don't need to be copied
|
435
|
-
next if name.
|
464
|
+
next if name.start_with?("sqlite_")
|
436
465
|
if to == "a#{from}"
|
437
466
|
name = "t#{name}"
|
438
467
|
elsif from == "a#{to}"
|
@@ -449,10 +478,10 @@ module ArJdbc
|
|
449
478
|
|
450
479
|
unless columns.empty?
|
451
480
|
# index name can't be the same
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
add_index(to, columns,
|
481
|
+
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
482
|
+
options[:unique] = true if index.unique
|
483
|
+
options[:where] = index.where if index.where
|
484
|
+
add_index(to, columns, **options)
|
456
485
|
end
|
457
486
|
end
|
458
487
|
end
|
@@ -517,7 +546,7 @@ module ArJdbc
|
|
517
546
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
518
547
|
end
|
519
548
|
|
520
|
-
basic_structure.map
|
549
|
+
basic_structure.map do |column|
|
521
550
|
column_name = column["name"]
|
522
551
|
|
523
552
|
if collation_hash.has_key? column_name
|
@@ -539,18 +568,6 @@ module ArJdbc
|
|
539
568
|
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
540
569
|
end
|
541
570
|
|
542
|
-
# DIFFERENCE: FQN
|
543
|
-
class SQLite3Integer < ::ActiveRecord::Type::Integer # :nodoc:
|
544
|
-
private
|
545
|
-
def _limit
|
546
|
-
# INTEGER storage class can be stored 8 bytes value.
|
547
|
-
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
548
|
-
limit || 8
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
# DIFFERENCE: FQN
|
553
|
-
::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
554
571
|
end
|
555
572
|
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
|
556
573
|
end
|
@@ -671,7 +688,9 @@ module ActiveRecord::ConnectionAdapters
|
|
671
688
|
end
|
672
689
|
|
673
690
|
def begin_isolated_db_transaction(isolation)
|
674
|
-
raise ActiveRecord::TransactionIsolationError,
|
691
|
+
raise ActiveRecord::TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
692
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
693
|
+
super
|
675
694
|
end
|
676
695
|
|
677
696
|
# SQLite driver doesn't support all types of insert statements with executeUpdate so
|
@@ -707,5 +726,23 @@ module ActiveRecord::ConnectionAdapters
|
|
707
726
|
total_sql
|
708
727
|
end
|
709
728
|
end
|
729
|
+
|
730
|
+
def initialize_type_map(m = type_map)
|
731
|
+
super
|
732
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
733
|
+
end
|
734
|
+
|
735
|
+
# DIFFERENCE: FQN
|
736
|
+
class SQLite3Integer < ::ActiveRecord::Type::Integer # :nodoc:
|
737
|
+
private
|
738
|
+
def _limit
|
739
|
+
# INTEGER storage class can be stored 8 bytes value.
|
740
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
741
|
+
limit || 8
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
# DIFFERENCE: FQN
|
746
|
+
::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
710
747
|
end
|
711
748
|
end
|
@@ -35,7 +35,17 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
35
35
|
# * http://sqlite.org/c3ref/open.html
|
36
36
|
# * http://sqlite.org/c3ref/c_open_autoproxy.html
|
37
37
|
# => 0x01 = readonly, 0x40 = uri (default in JDBC)
|
38
|
-
config[:properties][:open_mode] =
|
38
|
+
config[:properties][:open_mode] = ::SQLite3::Constants::Open::READONLY | ::SQLite3::Constants::Open::URI
|
39
|
+
end
|
40
|
+
|
41
|
+
if config[:flags]
|
42
|
+
config[:properties][:open_mode] ||= 0
|
43
|
+
config[:properties][:open_mode] |= config[:flags]
|
44
|
+
|
45
|
+
# JDBC driver has an extra flag for it
|
46
|
+
if config[:flags] & ::SQLite3::Constants::Open::SHAREDCACHE != 0
|
47
|
+
config[:properties][:shared_cache] = true
|
48
|
+
end
|
39
49
|
end
|
40
50
|
|
41
51
|
timeout = config[:timeout]
|
@@ -5,15 +5,17 @@ module ActiveRecord::Tasks
|
|
5
5
|
DatabaseTasks.module_eval do
|
6
6
|
|
7
7
|
# @override patched to adapt jdbc configuration
|
8
|
-
def each_current_configuration(environment,
|
8
|
+
def each_current_configuration(environment, name = nil)
|
9
9
|
environments = [environment]
|
10
10
|
environments << 'test' if environment == 'development'
|
11
11
|
|
12
12
|
environments.each do |env|
|
13
13
|
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
14
|
-
next if
|
14
|
+
next if name && name != db_config.name
|
15
15
|
|
16
|
-
|
16
|
+
if db_config.database
|
17
|
+
yield adapt_jdbc_config(db_config), db_config.name, env
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -21,21 +23,24 @@ module ActiveRecord::Tasks
|
|
21
23
|
# @override patched to adapt jdbc configuration
|
22
24
|
def each_local_configuration
|
23
25
|
ActiveRecord::Base.configurations.configs_for.each do |db_config|
|
24
|
-
next unless db_config.
|
26
|
+
next unless db_config.database
|
25
27
|
|
26
|
-
if local_database?(db_config
|
27
|
-
yield adapt_jdbc_config(db_config
|
28
|
+
if local_database?(db_config)
|
29
|
+
yield adapt_jdbc_config(db_config)
|
28
30
|
else
|
29
|
-
$stderr.puts "This task only modifies local databases. #{db_config.
|
31
|
+
$stderr.puts "This task only modifies local databases. #{db_config.database} is on a remote host."
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
36
|
private
|
35
37
|
|
36
|
-
def adapt_jdbc_config(
|
37
|
-
|
38
|
-
|
38
|
+
def adapt_jdbc_config(db_config)
|
39
|
+
if db_config.adapter.start_with? 'jdbc'
|
40
|
+
config = db_config.configuration_hash.merge(adapter: db_config.adapter.sub(/^jdbc/, ''))
|
41
|
+
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(db_config.env_name, db_config.name, config)
|
42
|
+
end
|
43
|
+
db_config
|
39
44
|
end
|
40
45
|
|
41
46
|
end
|
data/lib/arjdbc/version.rb
CHANGED
@@ -86,6 +86,7 @@ import org.jruby.anno.JRubyMethod;
|
|
86
86
|
import org.jruby.exceptions.RaiseException;
|
87
87
|
import org.jruby.ext.bigdecimal.RubyBigDecimal;
|
88
88
|
import org.jruby.ext.date.RubyDate;
|
89
|
+
import org.jruby.ext.date.RubyDateTime;
|
89
90
|
import org.jruby.javasupport.JavaEmbedUtils;
|
90
91
|
import org.jruby.javasupport.JavaUtil;
|
91
92
|
import org.jruby.runtime.Block;
|
@@ -124,6 +125,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
124
125
|
private IRubyObject config;
|
125
126
|
private IRubyObject adapter; // the AbstractAdapter instance we belong to
|
126
127
|
private volatile boolean connected = true;
|
128
|
+
private RubyClass attributeClass;
|
127
129
|
|
128
130
|
private boolean lazy = false; // final once set on initialize
|
129
131
|
private boolean jndi; // final once set on initialize
|
@@ -132,6 +134,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
132
134
|
|
133
135
|
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
134
136
|
super(runtime, metaClass);
|
137
|
+
attributeClass = runtime.getModule("ActiveModel").getClass("Attribute");
|
135
138
|
}
|
136
139
|
|
137
140
|
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
@@ -359,7 +362,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
359
362
|
if ( ! connection.getAutoCommit() ) {
|
360
363
|
try {
|
361
364
|
connection.commit();
|
362
|
-
resetSavepoints(context); // if any
|
365
|
+
resetSavepoints(context, connection); // if any
|
363
366
|
return context.runtime.newBoolean(true);
|
364
367
|
}
|
365
368
|
finally {
|
@@ -380,7 +383,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
380
383
|
if ( ! connection.getAutoCommit() ) {
|
381
384
|
try {
|
382
385
|
connection.rollback();
|
383
|
-
resetSavepoints(context); // if any
|
386
|
+
resetSavepoints(context, connection); // if any
|
384
387
|
return context.tru;
|
385
388
|
} finally {
|
386
389
|
connection.setAutoCommit(true);
|
@@ -516,7 +519,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
516
519
|
return null;
|
517
520
|
}
|
518
521
|
|
519
|
-
protected boolean resetSavepoints(final ThreadContext context) {
|
522
|
+
protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
|
520
523
|
if ( hasInternalVariable("savepoints") ) {
|
521
524
|
removeInternalVariable("savepoints");
|
522
525
|
return true;
|
@@ -610,11 +613,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
610
613
|
return convertJavaToRuby( connection.unwrap(Connection.class) );
|
611
614
|
}
|
612
615
|
}
|
613
|
-
catch (AbstractMethodError e) {
|
614
|
-
debugStackTrace(context, e);
|
615
|
-
warn(context, "driver/pool connection does not support unwrapping: " + e);
|
616
|
-
}
|
617
|
-
catch (SQLException e) {
|
616
|
+
catch (AbstractMethodError | SQLException e) {
|
618
617
|
debugStackTrace(context, e);
|
619
618
|
warn(context, "driver/pool connection does not support unwrapping: " + e);
|
620
619
|
}
|
@@ -860,27 +859,25 @@ public class RubyJdbcConnection extends RubyObject {
|
|
860
859
|
*/
|
861
860
|
@JRubyMethod(name = "execute_insert_pk", required = 2)
|
862
861
|
public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
|
863
|
-
return withConnection(context,
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
try {
|
868
|
-
|
869
|
-
statement = createStatement(context, connection);
|
862
|
+
return withConnection(context, connection -> {
|
863
|
+
Statement statement = null;
|
864
|
+
final String query = sqlString(sql);
|
865
|
+
try {
|
870
866
|
|
871
|
-
|
872
|
-
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
|
873
|
-
} else {
|
874
|
-
statement.executeUpdate(query, createStatementPk(pk));
|
875
|
-
}
|
867
|
+
statement = createStatement(context, connection);
|
876
868
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
} finally {
|
882
|
-
close(statement);
|
869
|
+
if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
|
870
|
+
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
|
871
|
+
} else {
|
872
|
+
statement.executeUpdate(query, createStatementPk(pk));
|
883
873
|
}
|
874
|
+
|
875
|
+
return mapGeneratedKeys(context, connection, statement);
|
876
|
+
} catch (final SQLException e) {
|
877
|
+
debugErrorSQL(context, query);
|
878
|
+
throw e;
|
879
|
+
} finally {
|
880
|
+
close(statement);
|
884
881
|
}
|
885
882
|
});
|
886
883
|
}
|
@@ -903,26 +900,24 @@ public class RubyJdbcConnection extends RubyObject {
|
|
903
900
|
@JRubyMethod(name = "execute_insert_pk", required = 3)
|
904
901
|
public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
|
905
902
|
final IRubyObject pk) {
|
906
|
-
return withConnection(context,
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
statement = connection.prepareStatement(query, createStatementPk(pk));
|
915
|
-
}
|
916
|
-
|
917
|
-
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
918
|
-
statement.executeUpdate();
|
919
|
-
return mapGeneratedKeys(context, connection, statement);
|
920
|
-
} catch (final SQLException e) {
|
921
|
-
debugErrorSQL(context, query);
|
922
|
-
throw e;
|
923
|
-
} finally {
|
924
|
-
close(statement);
|
903
|
+
return withConnection(context, connection -> {
|
904
|
+
PreparedStatement statement = null;
|
905
|
+
final String query = sqlString(sql);
|
906
|
+
try {
|
907
|
+
if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
|
908
|
+
statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
|
909
|
+
} else {
|
910
|
+
statement = connection.prepareStatement(query, createStatementPk(pk));
|
925
911
|
}
|
912
|
+
|
913
|
+
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
914
|
+
statement.executeUpdate();
|
915
|
+
return mapGeneratedKeys(context, connection, statement);
|
916
|
+
} catch (final SQLException e) {
|
917
|
+
debugErrorSQL(context, query);
|
918
|
+
throw e;
|
919
|
+
} finally {
|
920
|
+
close(statement);
|
926
921
|
}
|
927
922
|
});
|
928
923
|
}
|
@@ -1012,12 +1007,12 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1012
1007
|
binds = null;
|
1013
1008
|
} else { // (sql, binds)
|
1014
1009
|
maxRows = 0;
|
1015
|
-
binds = (RubyArray) TypeConverter.checkArrayType(args[1]);
|
1010
|
+
binds = (RubyArray) TypeConverter.checkArrayType(context, args[1]);
|
1016
1011
|
}
|
1017
1012
|
break;
|
1018
1013
|
case 3: // (sql, max_rows, binds)
|
1019
1014
|
maxRows = RubyNumeric.fix2int(args[1]);
|
1020
|
-
binds = (RubyArray) TypeConverter.checkArrayType(args[2]);
|
1015
|
+
binds = (RubyArray) TypeConverter.checkArrayType(context, args[2]);
|
1021
1016
|
break;
|
1022
1017
|
default: // (sql) 1-arg
|
1023
1018
|
maxRows = 0;
|
@@ -1106,6 +1101,28 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1106
1101
|
});
|
1107
1102
|
}
|
1108
1103
|
|
1104
|
+
@JRubyMethod(required = 1)
|
1105
|
+
public IRubyObject get_first_value(final ThreadContext context, final IRubyObject sql) {
|
1106
|
+
return withConnection(context, connection -> {
|
1107
|
+
Statement statement = null;
|
1108
|
+
final String query = sqlString(sql);
|
1109
|
+
try {
|
1110
|
+
statement = createStatement(context, connection);
|
1111
|
+
statement.execute(query);
|
1112
|
+
ResultSet rs = statement.getResultSet();
|
1113
|
+
if (rs == null || !rs.next()) return context.nil;
|
1114
|
+
|
1115
|
+
return jdbcToRuby(context, context.getRuntime(), 1, rs.getMetaData().getColumnType(1), rs);
|
1116
|
+
|
1117
|
+
} catch (final SQLException e) {
|
1118
|
+
debugErrorSQL(context, query);
|
1119
|
+
throw e;
|
1120
|
+
} finally {
|
1121
|
+
close(statement);
|
1122
|
+
}
|
1123
|
+
});
|
1124
|
+
}
|
1125
|
+
|
1109
1126
|
/**
|
1110
1127
|
* Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
|
1111
1128
|
* @param context which context this method is executing on.
|
@@ -2402,9 +2419,16 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2402
2419
|
final Connection connection, final PreparedStatement statement,
|
2403
2420
|
final int index, IRubyObject attribute) throws SQLException {
|
2404
2421
|
|
2405
|
-
|
2406
|
-
final int type
|
2407
|
-
|
2422
|
+
final IRubyObject value;
|
2423
|
+
final int type;
|
2424
|
+
|
2425
|
+
if (attributeClass.isInstance(attribute)) {
|
2426
|
+
type = jdbcTypeForAttribute(context, attribute);
|
2427
|
+
value = valueForDatabase(context, attribute);
|
2428
|
+
} else {
|
2429
|
+
type = jdbcTypeForPrimitiveAttribute(context, attribute);
|
2430
|
+
value = attribute;
|
2431
|
+
}
|
2408
2432
|
|
2409
2433
|
// All the set methods were calling this first so save a method call in the nil case
|
2410
2434
|
if ( value == context.nil ) {
|
@@ -2520,6 +2544,34 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2520
2544
|
return Types.OTHER; // -1 as well as 0 are used in Types
|
2521
2545
|
}
|
2522
2546
|
|
2547
|
+
protected String internedTypeForPrimitive(final ThreadContext context, final IRubyObject value) throws SQLException {
|
2548
|
+
if (value instanceof RubyString) {
|
2549
|
+
return "string";
|
2550
|
+
}
|
2551
|
+
if (value instanceof RubyInteger) {
|
2552
|
+
return "integer";
|
2553
|
+
}
|
2554
|
+
if (value instanceof RubyNumeric) {
|
2555
|
+
return "float";
|
2556
|
+
}
|
2557
|
+
if (value instanceof RubyTime || value instanceof RubyDateTime) {
|
2558
|
+
return "timestamp";
|
2559
|
+
}
|
2560
|
+
if (value instanceof RubyDate) {
|
2561
|
+
return "date";
|
2562
|
+
}
|
2563
|
+
if (value instanceof RubyBoolean) {
|
2564
|
+
return "boolean";
|
2565
|
+
}
|
2566
|
+
return "string";
|
2567
|
+
}
|
2568
|
+
|
2569
|
+
protected Integer jdbcTypeForPrimitiveAttribute(final ThreadContext context,
|
2570
|
+
final IRubyObject attribute) throws SQLException {
|
2571
|
+
final String internedType = internedTypeForPrimitive(context, attribute);
|
2572
|
+
return jdbcTypeFor(internedType);
|
2573
|
+
}
|
2574
|
+
|
2523
2575
|
protected Integer jdbcTypeFor(final String type) {
|
2524
2576
|
return JDBC_TYPE_FOR.get(type);
|
2525
2577
|
}
|
@@ -2531,7 +2583,9 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2531
2583
|
}
|
2532
2584
|
|
2533
2585
|
protected static IRubyObject attributeSQLType(final ThreadContext context, final IRubyObject attribute) {
|
2534
|
-
|
2586
|
+
final IRubyObject type = attributeType(context, attribute);
|
2587
|
+
if (type != null) return type.callMethod(context, "type");
|
2588
|
+
return context.nil;
|
2535
2589
|
}
|
2536
2590
|
|
2537
2591
|
private final CachingCallSite value_site = new FunctionalCachingCallSite("value"); // AR::Attribute#value
|
@@ -2544,23 +2598,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2544
2598
|
|
2545
2599
|
final IRubyObject value = value_site.call(context, attribute, attribute);
|
2546
2600
|
|
2547
|
-
|
2548
|
-
return "integer";
|
2549
|
-
}
|
2550
|
-
|
2551
|
-
if (value instanceof RubyNumeric) {
|
2552
|
-
return "float";
|
2553
|
-
}
|
2554
|
-
|
2555
|
-
if (value instanceof RubyTime) {
|
2556
|
-
return "timestamp";
|
2557
|
-
}
|
2558
|
-
|
2559
|
-
if (value instanceof RubyBoolean) {
|
2560
|
-
return "boolean";
|
2561
|
-
}
|
2562
|
-
|
2563
|
-
return "string";
|
2601
|
+
return internedTypeForPrimitive(context, value);
|
2564
2602
|
}
|
2565
2603
|
|
2566
2604
|
protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
|