activerecord-jdbc-adapter 60.0-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.
@@ -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
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  ArJdbc::ConnectionMethods.module_eval do
3
3
  def postgresql_connection(config)
4
+ config = config.deep_dup
4
5
  # NOTE: this isn't "really" necessary but Rails (in tests) assumes being able to :
5
6
  # ActiveRecord::Base.postgresql_connection ActiveRecord::Base.configurations['arunit'].merge(:insert_returning => false)
6
7
  # ... while using symbols by default but than configurations returning string keys ;(
@@ -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::SpecializedString.new(:macaddr)
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 |_, _, sql_type|
158
+ m.register_type 'interval' do |*args, sql_type|
159
159
  precision = extract_precision(sql_type)
160
- OID::SpecializedString.new(:interval, precision: precision)
160
+ OID::Interval.new(precision: precision)
161
161
  end
162
162
 
163
163
  register_class_with_precision m, 'time', Type::Time
@@ -213,7 +213,7 @@ module ArJdbc
213
213
  if oid
214
214
  if oid.is_a? Numeric || oid.match(/^\d+$/)
215
215
  # numeric OID
216
- query += "WHERE t.oid::integer = %s" % oid
216
+ query += "WHERE t.oid = %s" % oid
217
217
 
218
218
  elsif m = oid.match(/"?(\w+)"?\."?(\w+)"?/)
219
219
  # namespace and type name
@@ -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
@@ -97,6 +132,10 @@ module ArJdbc
97
132
  true
98
133
  end
99
134
 
135
+ def supports_common_table_expressions?
136
+ database_version >= "3.8.3"
137
+ end
138
+
100
139
  def supports_insert_on_conflict?
101
140
  database_version >= "3.24.0"
102
141
  end
@@ -110,13 +149,6 @@ module ArJdbc
110
149
  true
111
150
  end
112
151
 
113
- # Returns 62. SQLite supports index names up to 64
114
- # characters. The rest is used by Rails internally to perform
115
- # temporary rename operations
116
- def allowed_index_name_length
117
- index_name_length - 2
118
- end
119
-
120
152
  def native_database_types #:nodoc:
121
153
  NATIVE_DATABASE_TYPES
122
154
  end
@@ -154,7 +186,9 @@ module ArJdbc
154
186
  # DATABASE STATEMENTS ======================================
155
187
  #++
156
188
 
157
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
189
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
190
+ :pragma
191
+ ) # :nodoc:
158
192
  private_constant :READ_QUERY
159
193
 
160
194
  def write_query?(sql) # :nodoc:
@@ -181,15 +215,15 @@ module ArJdbc
181
215
  #def execute(sql, name = nil) #:nodoc:
182
216
 
183
217
  def begin_db_transaction #:nodoc:
184
- log("begin transaction", nil) { @connection.transaction }
218
+ log("begin transaction", 'TRANSACTION') { @connection.transaction }
185
219
  end
186
220
 
187
221
  def commit_db_transaction #:nodoc:
188
- log("commit transaction", nil) { @connection.commit }
222
+ log("commit transaction", 'TRANSACTION') { @connection.commit }
189
223
  end
190
224
 
191
225
  def exec_rollback_db_transaction #:nodoc:
192
- log("rollback transaction", nil) { @connection.rollback }
226
+ log("rollback transaction", 'TRANSACTION') { @connection.rollback }
193
227
  end
194
228
 
195
229
  # SCHEMA STATEMENTS ========================================
@@ -199,8 +233,11 @@ module ArJdbc
199
233
  pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
200
234
  end
201
235
 
202
- def remove_index(table_name, options = {}) #:nodoc:
203
- index_name = index_name_for_remove(table_name, options)
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
+
204
241
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
205
242
  end
206
243
 
@@ -209,21 +246,23 @@ module ArJdbc
209
246
  # Example:
210
247
  # rename_table('octopuses', 'octopi')
211
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)
212
251
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
213
252
  rename_table_indexes(table_name, new_name)
214
253
  end
215
254
 
216
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
255
+ def add_column(table_name, column_name, type, **options) #:nodoc:
217
256
  if invalid_alter_table_type?(type, options)
218
257
  alter_table(table_name) do |definition|
219
- definition.column(column_name, type, options)
258
+ definition.column(column_name, type, **options)
220
259
  end
221
260
  else
222
261
  super
223
262
  end
224
263
  end
225
264
 
226
- def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
265
+ def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
227
266
  alter_table(table_name) do |definition|
228
267
  definition.remove_column column_name
229
268
  definition.foreign_keys.delete_if do |_, fk_options|
@@ -249,16 +288,11 @@ module ArJdbc
249
288
  end
250
289
  end
251
290
 
252
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
291
+ def change_column(table_name, column_name, type, **options) #:nodoc:
253
292
  alter_table(table_name) do |definition|
254
293
  definition[column_name].instance_eval do
255
294
  self.type = type
256
- self.limit = options[:limit] if options.include?(:limit)
257
- self.default = options[:default] if options.include?(:default)
258
- self.null = options[:null] if options.include?(:null)
259
- self.precision = options[:precision] if options.include?(:precision)
260
- self.scale = options[:scale] if options.include?(:scale)
261
- self.collation = options[:collation] if options.include?(:collation)
295
+ self.options.merge!(options)
262
296
  end
263
297
  end
264
298
  end
@@ -307,21 +341,23 @@ module ArJdbc
307
341
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
308
342
  elsif insert.update_duplicates?
309
343
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
344
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
310
345
  sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
311
346
  end
312
347
 
313
348
  sql
314
349
  end
315
350
 
351
+ def shared_cache?
352
+ config[:properties] && config[:properties][:shared_cache] == true
353
+ end
354
+
316
355
  def get_database_version # :nodoc:
317
356
  SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
318
357
  end
319
358
 
320
- def build_truncate_statements(*table_names)
321
- truncate_tables = table_names.map do |table_name|
322
- "DELETE FROM #{quote_table_name(table_name)}"
323
- end
324
- combine_multi_statements(truncate_tables)
359
+ def build_truncate_statement(table_name)
360
+ "DELETE FROM #{quote_table_name(table_name)}"
325
361
  end
326
362
 
327
363
  def check_version
@@ -337,11 +373,6 @@ module ArJdbc
337
373
  999
338
374
  end
339
375
 
340
- def initialize_type_map(m = type_map)
341
- super
342
- register_class_with_limit m, %r(int)i, SQLite3Integer
343
- end
344
-
345
376
  def table_structure(table_name)
346
377
  structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
347
378
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
@@ -352,10 +383,16 @@ module ArJdbc
352
383
  # See: https://www.sqlite.org/lang_altertable.html
353
384
  # SQLite has an additional restriction on the ALTER TABLE statement
354
385
  def invalid_alter_table_type?(type, options)
355
- type.to_sym == :primary_key || options[:primary_key]
386
+ type.to_sym == :primary_key || options[:primary_key] ||
387
+ options[:null] == false && options[:default].nil?
356
388
  end
357
389
 
358
- def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
390
+ def alter_table(
391
+ table_name,
392
+ foreign_keys = foreign_keys(table_name),
393
+ check_constraints = check_constraints(table_name),
394
+ **options
395
+ )
359
396
  altered_table_name = "a#{table_name}"
360
397
 
361
398
  caller = lambda do |definition|
@@ -365,7 +402,11 @@ module ArJdbc
365
402
  fk.options[:column] = column
366
403
  end
367
404
  to_table = strip_table_name_prefix_and_suffix(fk.to_table)
368
- 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)
369
410
  end
370
411
 
371
412
  yield definition if block_given?
@@ -387,11 +428,12 @@ module ArJdbc
387
428
  def copy_table(from, to, options = {})
388
429
  from_primary_key = primary_key(from)
389
430
  options[:id] = false
390
- create_table(to, options) do |definition|
431
+ create_table(to, **options) do |definition|
391
432
  @definition = definition
392
433
  if from_primary_key.is_a?(Array)
393
434
  @definition.primary_keys from_primary_key
394
435
  end
436
+
395
437
  columns(from).each do |column|
396
438
  column_name = options[:rename] ?
397
439
  (options[:rename][column.name] ||
@@ -419,7 +461,7 @@ module ArJdbc
419
461
  name = index.name
420
462
  # indexes sqlite creates for internal use start with `sqlite_` and
421
463
  # don't need to be copied
422
- next if name.starts_with?("sqlite_")
464
+ next if name.start_with?("sqlite_")
423
465
  if to == "a#{from}"
424
466
  name = "t#{name}"
425
467
  elsif from == "a#{to}"
@@ -436,10 +478,10 @@ module ArJdbc
436
478
 
437
479
  unless columns.empty?
438
480
  # index name can't be the same
439
- opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
440
- opts[:unique] = true if index.unique
441
- opts[:where] = index.where if index.where
442
- add_index(to, columns, opts)
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)
443
485
  end
444
486
  end
445
487
  end
@@ -504,7 +546,7 @@ module ArJdbc
504
546
  collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
505
547
  end
506
548
 
507
- basic_structure.map! do |column|
549
+ basic_structure.map do |column|
508
550
  column_name = column["name"]
509
551
 
510
552
  if collation_hash.has_key? column_name
@@ -526,18 +568,6 @@ module ArJdbc
526
568
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
527
569
  end
528
570
 
529
- # DIFFERENCE: FQN
530
- class SQLite3Integer < ::ActiveRecord::Type::Integer # :nodoc:
531
- private
532
- def _limit
533
- # INTEGER storage class can be stored 8 bytes value.
534
- # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
535
- limit || 8
536
- end
537
- end
538
-
539
- # DIFFERENCE: FQN
540
- ::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
541
571
  end
542
572
  # DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
543
573
  end
@@ -658,7 +688,9 @@ module ActiveRecord::ConnectionAdapters
658
688
  end
659
689
 
660
690
  def begin_isolated_db_transaction(isolation)
661
- raise ActiveRecord::TransactionIsolationError, 'adapter does not support setting transaction isolation'
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
662
694
  end
663
695
 
664
696
  # SQLite driver doesn't support all types of insert statements with executeUpdate so
@@ -694,5 +726,23 @@ module ActiveRecord::ConnectionAdapters
694
726
  total_sql
695
727
  end
696
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)
697
747
  end
698
748
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  ArJdbc::ConnectionMethods.module_eval do
3
3
  def sqlite3_connection(config)
4
+ config = config.deep_dup
4
5
  config[:adapter_spec] ||= ::ArJdbc::SQLite3
5
6
  config[:adapter_class] = ActiveRecord::ConnectionAdapters::SQLite3Adapter unless config.key?(:adapter_class)
6
7
 
@@ -34,7 +35,17 @@ ArJdbc::ConnectionMethods.module_eval do
34
35
  # * http://sqlite.org/c3ref/open.html
35
36
  # * http://sqlite.org/c3ref/c_open_autoproxy.html
36
37
  # => 0x01 = readonly, 0x40 = uri (default in JDBC)
37
- config[:properties][:open_mode] = 0x01 | 0x40
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
38
49
  end
39
50
 
40
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, spec_name = nil)
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 spec_name && spec_name != db_config.spec_name
14
+ next if name && name != db_config.name
15
15
 
16
- yield adapt_jdbc_config(db_config.config), db_config.spec_name, env unless db_config.config['database'].blank?
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.config['database']
26
+ next unless db_config.database
25
27
 
26
- if local_database?(db_config.config)
27
- yield adapt_jdbc_config(db_config.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.config['database']} is on a remote host."
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(config)
37
- return config unless config['adapter']
38
- config.merge 'adapter' => config['adapter'].sub(/^jdbc/, '')
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