activerecord-jdbc-alt-adapter 61.0.0-java → 70.0.0.rc1-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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +273 -0
  3. data/.gitignore +1 -0
  4. data/Gemfile +8 -6
  5. data/README.md +2 -1
  6. data/Rakefile +1 -1
  7. data/activerecord-jdbc-adapter.gemspec +2 -2
  8. data/activerecord-jdbc-alt-adapter.gemspec +2 -2
  9. data/lib/arel/visitors/compat.rb +5 -33
  10. data/lib/arel/visitors/h2.rb +1 -13
  11. data/lib/arel/visitors/hsqldb.rb +1 -21
  12. data/lib/arel/visitors/sql_server.rb +2 -103
  13. data/lib/arjdbc/abstract/core.rb +8 -9
  14. data/lib/arjdbc/abstract/database_statements.rb +4 -4
  15. data/lib/arjdbc/discover.rb +0 -67
  16. data/lib/arjdbc/hsqldb/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +3 -3
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  20. data/lib/arjdbc/jdbc/column.rb +1 -26
  21. data/lib/arjdbc/jdbc/type_cast.rb +2 -2
  22. data/lib/arjdbc/jdbc.rb +0 -7
  23. data/lib/arjdbc/mssql/adapter.rb +134 -105
  24. data/lib/arjdbc/mssql/quoting.rb +26 -27
  25. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  26. data/lib/arjdbc/mssql/schema_definitions.rb +32 -17
  27. data/lib/arjdbc/mssql/schema_dumper.rb +13 -1
  28. data/lib/arjdbc/mssql/schema_statements.rb +61 -36
  29. data/lib/arjdbc/mssql/transaction.rb +2 -2
  30. data/lib/arjdbc/mssql/types/date_and_time_types.rb +6 -6
  31. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -2
  32. data/lib/arjdbc/mssql.rb +1 -1
  33. data/lib/arjdbc/mysql/adapter.rb +2 -1
  34. data/lib/arjdbc/oracle/adapter.rb +4 -23
  35. data/lib/arjdbc/postgresql/adapter.rb +64 -1
  36. data/lib/arjdbc/postgresql/oid_types.rb +68 -47
  37. data/lib/arjdbc/sqlite3/adapter.rb +132 -88
  38. data/lib/arjdbc/tasks/database_tasks.rb +0 -12
  39. data/lib/arjdbc/util/serialized_attributes.rb +0 -22
  40. data/lib/arjdbc/util/table_copier.rb +2 -1
  41. data/lib/arjdbc/version.rb +1 -1
  42. data/rakelib/02-test.rake +3 -18
  43. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -2
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +14 -1
  45. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +33 -0
  46. metadata +8 -40
  47. data/lib/active_record/connection_adapters/as400_adapter.rb +0 -2
  48. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -1
  49. data/lib/active_record/connection_adapters/derby_adapter.rb +0 -1
  50. data/lib/active_record/connection_adapters/informix_adapter.rb +0 -1
  51. data/lib/arel/visitors/db2.rb +0 -137
  52. data/lib/arel/visitors/derby.rb +0 -112
  53. data/lib/arel/visitors/firebird.rb +0 -79
  54. data/lib/arjdbc/db2/adapter.rb +0 -808
  55. data/lib/arjdbc/db2/as400.rb +0 -142
  56. data/lib/arjdbc/db2/column.rb +0 -131
  57. data/lib/arjdbc/db2/connection_methods.rb +0 -48
  58. data/lib/arjdbc/db2.rb +0 -4
  59. data/lib/arjdbc/derby/active_record_patch.rb +0 -13
  60. data/lib/arjdbc/derby/adapter.rb +0 -521
  61. data/lib/arjdbc/derby/connection_methods.rb +0 -20
  62. data/lib/arjdbc/derby/schema_creation.rb +0 -15
  63. data/lib/arjdbc/derby.rb +0 -3
  64. data/lib/arjdbc/firebird/adapter.rb +0 -413
  65. data/lib/arjdbc/firebird/connection_methods.rb +0 -23
  66. data/lib/arjdbc/firebird.rb +0 -4
  67. data/lib/arjdbc/informix/adapter.rb +0 -139
  68. data/lib/arjdbc/informix/connection_methods.rb +0 -9
  69. data/lib/arjdbc/sybase/adapter.rb +0 -47
  70. data/lib/arjdbc/sybase.rb +0 -2
  71. data/lib/arjdbc/tasks/db2_database_tasks.rb +0 -104
  72. data/lib/arjdbc/tasks/derby_database_tasks.rb +0 -95
  73. data/src/java/arjdbc/derby/DerbyModule.java +0 -178
  74. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +0 -152
  75. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +0 -174
  76. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +0 -75
@@ -14,80 +14,95 @@ module ActiveRecord
14
14
 
15
15
  # datetime with seconds always zero (:00) and without fractional seconds
16
16
  def smalldatetime(*args, **options)
17
- args.each { |name| column(name, :smalldatetime, options) }
17
+ args.each { |name| column(name, :smalldatetime, **options) }
18
18
  end
19
19
 
20
20
  # this is the old sql server datetime type, the precision is as follow
21
21
  # xx1, xx3, and xx7
22
22
  def datetime_basic(*args, **options)
23
- args.each { |name| column(name, :datetime_basic, options) }
23
+ args.each { |name| column(name, :datetime_basic, **options) }
24
24
  end
25
25
 
26
26
  def real(*args, **options)
27
- args.each { |name| column(name, :real, options) }
27
+ args.each { |name| column(name, :real, **options) }
28
28
  end
29
29
 
30
30
  def money(*args, **options)
31
- args.each { |name| column(name, :money, options) }
31
+ args.each { |name| column(name, :money, **options) }
32
32
  end
33
33
 
34
34
  def smallmoney(*args, **options)
35
- args.each { |name| column(name, :smallmoney, options) }
35
+ args.each { |name| column(name, :smallmoney, **options) }
36
36
  end
37
37
 
38
38
  def char(*args, **options)
39
- args.each { |name| column(name, :char, options) }
39
+ args.each { |name| column(name, :char, **options) }
40
40
  end
41
41
 
42
42
  def varchar(*args, **options)
43
- args.each { |name| column(name, :varchar, options) }
43
+ args.each { |name| column(name, :varchar, **options) }
44
44
  end
45
45
 
46
46
  def varchar_max(*args, **options)
47
- args.each { |name| column(name, :varchar_max, options) }
47
+ args.each { |name| column(name, :varchar_max, **options) }
48
48
  end
49
49
 
50
50
  def text_basic(*args, **options)
51
- args.each { |name| column(name, :text_basic, options) }
51
+ args.each { |name| column(name, :text_basic, **options) }
52
52
  end
53
53
 
54
54
  def nchar(*args, **options)
55
- args.each { |name| column(name, :nchar, options) }
55
+ args.each { |name| column(name, :nchar, **options) }
56
56
  end
57
57
 
58
58
  def ntext(*args, **options)
59
- args.each { |name| column(name, :ntext, options) }
59
+ args.each { |name| column(name, :ntext, **options) }
60
60
  end
61
61
 
62
62
  def binary_basic(*args, **options)
63
- args.each { |name| column(name, :binary_basic, options) }
63
+ args.each { |name| column(name, :binary_basic, **options) }
64
64
  end
65
65
 
66
66
  def varbinary(*args, **options)
67
- args.each { |name| column(name, :varbinary, options) }
67
+ args.each { |name| column(name, :varbinary, **options) }
68
68
  end
69
69
 
70
70
  def uuid(*args, **options)
71
- args.each { |name| column(name, :uniqueidentifier, options) }
71
+ args.each { |name| column(name, :uniqueidentifier, **options) }
72
72
  end
73
73
  end
74
74
 
75
75
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
76
76
  include ColumnMethods
77
77
 
78
+ def column(name, type, index: nil, **options)
79
+ # TODO: remove this when the below changed is released
80
+ # Fix erroneous nil default precision on virtual datetime columns #46110
81
+ # https://github.com/rails/rails/pull/46110
82
+ #
83
+ if @conn.supports_datetime_with_precision?
84
+ if type == :datetime && !options.key?(:precision)
85
+ options[:precision] = 7
86
+ end
87
+ end
88
+
89
+ super
90
+ end
91
+
92
+
78
93
  def new_column_definition(name, type, **options)
79
94
  case type
80
95
  when :primary_key
81
96
  options[:is_identity] = true
97
+ when :datetime
98
+ options[:precision] = 7 if !options.key?(:precision) && @conn.supports_datetime_with_precision?
82
99
  end
83
100
 
84
101
  super
85
102
  end
86
103
 
87
104
  def timestamps(**options)
88
- if !options.key?(:precision) && @conn.supports_datetime_with_precision?
89
- options[:precision] = 7
90
- end
105
+ options[:precision] = 7 if !options.key?(:precision) && @conn.supports_datetime_with_precision?
91
106
 
92
107
  super
93
108
  end
@@ -28,11 +28,23 @@ module ActiveRecord
28
28
  super && column.identity?
29
29
  end
30
30
 
31
+ def schema_precision(column)
32
+ case column.type
33
+ when :datetime
34
+ if column.precision == 7
35
+ nil
36
+ else
37
+ column.precision.inspect
38
+ end
39
+ else
40
+ super
41
+ end
42
+ end
43
+
31
44
  # def schema_collation(column)
32
45
  # return unless column.collation
33
46
  # column.collation if column.collation != collation
34
47
  # end
35
-
36
48
  end
37
49
  end
38
50
  end
@@ -7,36 +7,36 @@ module ActiveRecord
7
7
 
8
8
  NATIVE_DATABASE_TYPES = {
9
9
  # Logical Rails types to SQL Server types
10
- primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
11
- integer: { name: 'int', limit: 4 },
12
- boolean: { name: 'bit' },
13
- decimal: { name: 'decimal' },
14
- float: { name: 'float' },
15
- date: { name: 'date' },
16
- time: { name: 'time' },
17
- datetime: { name: 'datetime2' },
18
- string: { name: 'nvarchar', limit: 4000 },
19
- text: { name: 'nvarchar(max)' },
20
- binary: { name: 'varbinary(max)' },
10
+ primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
11
+ integer: { name: 'int', limit: 4 },
12
+ boolean: { name: 'bit' },
13
+ decimal: { name: 'decimal' },
14
+ float: { name: 'float' },
15
+ date: { name: 'date' },
16
+ time: { name: 'time' },
17
+ datetime: { name: 'datetime2' },
18
+ string: { name: 'nvarchar', limit: 4000 },
19
+ text: { name: 'nvarchar(max)' },
20
+ binary: { name: 'varbinary(max)' },
21
21
  # Other types or SQL Server specific
22
- bigint: { name: 'bigint' },
23
- smalldatetime: { name: 'smalldatetime' },
22
+ bigint: { name: 'bigint' },
23
+ smalldatetime: { name: 'smalldatetime' },
24
24
  datetime_basic: { name: 'datetime' },
25
- timestamp: { name: 'datetime' },
26
- real: { name: 'real' },
27
- money: { name: 'money' },
28
- smallmoney: { name: 'smallmoney' },
29
- char: { name: 'char' },
30
- nchar: { name: 'nchar' },
31
- varchar: { name: 'varchar', limit: 8000 },
32
- varchar_max: { name: 'varchar(max)' },
33
- uuid: { name: 'uniqueidentifier' },
34
- binary_basic: { name: 'binary' },
35
- varbinary: { name: 'varbinary', limit: 8000 },
25
+ timestamp: { name: 'datetime' },
26
+ real: { name: 'real' },
27
+ money: { name: 'money' },
28
+ smallmoney: { name: 'smallmoney' },
29
+ char: { name: 'char' },
30
+ nchar: { name: 'nchar' },
31
+ varchar: { name: 'varchar', limit: 8000 },
32
+ varchar_max: { name: 'varchar(max)' },
33
+ uuid: { name: 'uniqueidentifier' },
34
+ binary_basic: { name: 'binary' },
35
+ varbinary: { name: 'varbinary', limit: 8000 },
36
36
  # Deprecated SQL Server types
37
- image: { name: 'image' },
38
- ntext: { name: 'ntext' },
39
- text_basic: { name: 'text' }
37
+ image: { name: 'image' },
38
+ ntext: { name: 'ntext' },
39
+ text_basic: { name: 'text' }
40
40
  }.freeze
41
41
 
42
42
  def native_database_types
@@ -127,9 +127,17 @@ module ActiveRecord
127
127
  create_database(name, options)
128
128
  end
129
129
 
130
- def remove_column(table_name, column_name, type = nil, options = {})
131
- raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
130
+ def remove_columns(table_name, *column_names, type: nil, **options)
131
+ if column_names.empty?
132
+ raise ArgumentError.new('You must specify at least one column name. Example: remove_columns(:people, :first_name)')
133
+ end
134
+
135
+ column_names.each do |column_name|
136
+ remove_column(table_name, column_name, type, **options)
137
+ end
138
+ end
132
139
 
140
+ def remove_column(table_name, column_name, _type = nil, **options)
133
141
  return if options[:if_exists] == true && !column_exists?(table_name, column_name)
134
142
 
135
143
  remove_check_constraints(table_name, column_name)
@@ -138,7 +146,7 @@ module ActiveRecord
138
146
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
139
147
  end
140
148
 
141
- def drop_table(table_name, options = {})
149
+ def drop_table(table_name, **options)
142
150
  # mssql cannot recreate referenced table with force: :cascade
143
151
  # https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-2017
144
152
  if options[:force] == :cascade
@@ -248,7 +256,7 @@ module ActiveRecord
248
256
  (order_columns << super).join(', ')
249
257
  end
250
258
 
251
- def add_timestamps(table_name, options = {})
259
+ def add_timestamps(table_name, **options)
252
260
  if !options.key?(:precision) && supports_datetime_with_precision?
253
261
  options[:precision] = 7
254
262
  end
@@ -256,6 +264,16 @@ module ActiveRecord
256
264
  super
257
265
  end
258
266
 
267
+ def add_column(table_name, column_name, type, **options)
268
+ if supports_datetime_with_precision?
269
+ if type == :datetime && !options.key?(:precision)
270
+ options[:precision] = 7
271
+ end
272
+ end
273
+
274
+ super
275
+ end
276
+
259
277
  def create_schema_dumper(options)
260
278
  MSSQL::SchemaDumper.create(self, options)
261
279
  end
@@ -323,16 +341,20 @@ module ActiveRecord
323
341
  quoted_table = quote_table_name(table_name)
324
342
  quoted_column = quote_column_name(column_name)
325
343
  quoted_default = quote(default)
344
+
326
345
  unless null || default.nil?
327
346
  execute("UPDATE #{quoted_table} SET #{quoted_column}=#{quoted_default} WHERE #{quoted_column} IS NULL")
328
347
  end
348
+
349
+ options = { limit: column.limit, precision: column.precision, scale: column.scale }
350
+
329
351
  sql_alter = [
330
352
  "ALTER TABLE #{quoted_table}",
331
- "ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, limit: column.limit, precision: column.precision, scale: column.scale)}",
332
- (' NOT NULL' unless null)
353
+ "ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, **options)}",
354
+ ('NOT NULL' unless null)
333
355
  ]
334
356
 
335
- execute(sql_alter.join(' '))
357
+ execute(sql_alter.compact.join(' '))
336
358
  end
337
359
 
338
360
  def update_table_definition(table_name, base) #:nodoc:
@@ -345,11 +367,14 @@ module ActiveRecord
345
367
  MSSQL::SchemaCreation.new(self)
346
368
  end
347
369
 
348
- def create_table_definition(*args)
349
- MSSQL::TableDefinition.new(self, *args)
370
+ def create_table_definition(name, **options)
371
+ MSSQL::TableDefinition.new(self, name, **options)
350
372
  end
351
373
 
352
374
  def new_column_from_field(table_name, field)
375
+ # NOTE: this method is used by the columns method in the abstract Class
376
+ # to map column_definitions. It would be good if column_definitions is
377
+ # implemented in ruby
353
378
  field
354
379
  end
355
380
 
@@ -27,10 +27,10 @@ module ActiveRecord
27
27
  module RealTransactionExt
28
28
  attr_reader :initial_transaction_isolation
29
29
 
30
- def initialize(connection, options, *args)
30
+ def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
31
31
  @connection = connection
32
32
 
33
- if options[:isolation]
33
+ if isolation
34
34
  @initial_transaction_isolation = current_transaction_isolation
35
35
  end
36
36
 
@@ -13,9 +13,9 @@ module ActiveRecord
13
13
  return %("#{value}") if value.acts_like?(:string)
14
14
 
15
15
  if value.usec > 0
16
- %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
16
+ %("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
17
17
  else
18
- %("#{value.to_s(:db)}")
18
+ %("#{value.to_fs(:db)}")
19
19
  end
20
20
  end
21
21
 
@@ -54,9 +54,9 @@ module ActiveRecord
54
54
  return %("#{value}") if value.acts_like?(:string)
55
55
 
56
56
  if value.usec > 0
57
- %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
57
+ %("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
58
58
  else
59
- %("#{value.to_s(:db)}")
59
+ %("#{value.to_fs(:db)}")
60
60
  end
61
61
  end
62
62
 
@@ -100,9 +100,9 @@ module ActiveRecord
100
100
  return %("#{value}") if value.acts_like?(:string)
101
101
 
102
102
  if value.usec > 0
103
- %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
103
+ %("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
104
104
  else
105
- %("#{value.to_s(:db)}")
105
+ %("#{value.to_fs(:db)}")
106
106
  end
107
107
  end
108
108
 
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  end
36
36
 
37
37
  class Money < Decimal
38
- def initialize(options = {})
38
+ def initialize(precision: nil, limit: nil, scale: nil)
39
39
  super
40
40
  @precision = 19
41
41
  @scale = 4
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  class SmallMoney < Decimal
49
- def initialize(options = {})
49
+ def initialize(precision: nil, limit: nil, scale: nil)
50
50
  super
51
51
  @precision = 10
52
52
  @scale = 4
data/lib/arjdbc/mssql.rb CHANGED
@@ -6,4 +6,4 @@ module ArJdbc
6
6
  MsSQL = MSSQL # compatibility with 1.2
7
7
  end
8
8
 
9
- ArJdbc.warn_unsupported_adapter 'mssql', [6, 1] # warns on AR >= 4.2
9
+ ArJdbc.warn_unsupported_adapter 'mssql', [7, 0] # warns on AR >= 4.2
@@ -107,7 +107,8 @@ module ActiveRecord
107
107
 
108
108
  # Reloading the type map in abstract/statement_cache.rb blows up postgres
109
109
  def clear_cache!
110
- reload_type_map
110
+ # FIXME: This seems to have disappeared in Rails 7?
111
+ # reload_type_map
111
112
  super
112
113
  end
113
114
 
@@ -40,7 +40,7 @@ module ArJdbc
40
40
  return if @@_initialized; @@_initialized = true
41
41
 
42
42
  require 'arjdbc/util/serialized_attributes'
43
- Util::SerializedAttributes.setup /LOB\(|LOB$/i, 'after_save_with_oracle_lob'
43
+ Util::SerializedAttributes.setup %r{LOB\(|LOB$}i, 'after_save_with_oracle_lob'
44
44
 
45
45
  unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
46
46
  instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
@@ -285,7 +285,7 @@ module ArJdbc
285
285
  execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
286
286
  end
287
287
  end
288
- end if AR42
288
+ end
289
289
 
290
290
  # @private
291
291
  def add_index_options(table_name, column_name, options = {})
@@ -309,7 +309,7 @@ module ArJdbc
309
309
 
310
310
  quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
311
311
  [ index_name, index_type, quoted_column_names, tablespace, index_options ]
312
- end if AR42
312
+ end
313
313
 
314
314
  # @override
315
315
  def remove_index(table_name, options = {})
@@ -327,12 +327,7 @@ module ArJdbc
327
327
  end
328
328
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
329
329
  execute "DROP INDEX #{quote_column_name(index_name)}"
330
- end if AR42
331
-
332
- # @private
333
- def remove_index(table_name, options = {})
334
- execute "DROP INDEX #{index_name(table_name, options)}"
335
- end unless AR42
330
+ end
336
331
 
337
332
  def change_column_default(table_name, column_name, default)
338
333
  execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
@@ -361,25 +356,11 @@ module ArJdbc
361
356
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
362
357
  end
363
358
 
364
- if ActiveRecord::VERSION::MAJOR >= 4
365
-
366
359
  # @override
367
360
  def remove_column(table_name, column_name, type = nil, options = {})
368
361
  do_remove_column(table_name, column_name)
369
362
  end
370
363
 
371
- else
372
-
373
- # @override
374
- def remove_column(table_name, *column_names)
375
- for column_name in column_names.flatten
376
- do_remove_column(table_name, column_name)
377
- end
378
- end
379
- alias remove_columns remove_column
380
-
381
- end
382
-
383
364
  def do_remove_column(table_name, column_name)
384
365
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
385
366
  end
@@ -81,7 +81,7 @@ module ArJdbc
81
81
  # If using Active Record's time zone support configure the connection to return
82
82
  # TIMESTAMP WITH ZONE types in UTC.
83
83
  # (SET TIME ZONE does not use an equals sign like other SET variables)
84
- if ActiveRecord::Base.default_timezone == :utc
84
+ if ActiveRecord.default_timezone == :utc
85
85
  execute("SET time zone 'UTC'", 'SCHEMA')
86
86
  elsif tz = local_tz
87
87
  execute("SET time zone '#{tz}'", 'SCHEMA')
@@ -320,6 +320,38 @@ module ArJdbc
320
320
  exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
321
321
  end
322
322
 
323
+ # Returns a list of defined enum types, and their values.
324
+ def enum_types
325
+ query = <<~SQL
326
+ SELECT
327
+ type.typname AS name,
328
+ string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
329
+ FROM pg_enum AS enum
330
+ JOIN pg_type AS type
331
+ ON (type.oid = enum.enumtypid)
332
+ GROUP BY type.typname;
333
+ SQL
334
+ exec_query(query, "SCHEMA").cast_values
335
+ end
336
+
337
+ # Given a name and an array of values, creates an enum type.
338
+ def create_enum(name, values)
339
+ sql_values = values.map { |s| "'#{s}'" }.join(", ")
340
+ query = <<~SQL
341
+ DO $$
342
+ BEGIN
343
+ IF NOT EXISTS (
344
+ SELECT 1 FROM pg_type t
345
+ WHERE t.typname = '#{name}'
346
+ ) THEN
347
+ CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
348
+ END IF;
349
+ END
350
+ $$;
351
+ SQL
352
+ exec_query(query)
353
+ end
354
+
323
355
  # Returns the configured supported identifier length supported by PostgreSQL
324
356
  def max_identifier_length
325
357
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
@@ -672,6 +704,37 @@ module ActiveRecord::ConnectionAdapters
672
704
  class PostgreSQLAdapter < AbstractAdapter
673
705
  class_attribute :create_unlogged_tables, default: false
674
706
 
707
+ ##
708
+ # :singleton-method:
709
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
710
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
711
+ # but significantly increases the risk of data loss if the database
712
+ # crashes. As a result, this should not be used in production
713
+ # environments. If you would like all created tables to be unlogged in
714
+ # the test environment you can add the following line to your test.rb
715
+ # file:
716
+ #
717
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
718
+ class_attribute :create_unlogged_tables, default: false
719
+
720
+ ##
721
+ # :singleton-method:
722
+ # PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
723
+ # in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
724
+ # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
725
+ # store DateTimes as "timestamp with time zone":
726
+ #
727
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
728
+ #
729
+ # Or if you are adding a custom type:
730
+ #
731
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
732
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
733
+ #
734
+ # If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
735
+ # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
736
+ class_attribute :datetime_type, default: :timestamp
737
+
675
738
  # Try to use as much of the built in postgres logic as possible
676
739
  # maybe someday we can extend the actual adapter
677
740
  include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
@@ -116,54 +116,57 @@ module ArJdbc
116
116
 
117
117
  private
118
118
 
119
- def initialize_type_map(m = type_map)
120
- register_class_with_limit m, 'int2', Type::Integer
121
- register_class_with_limit m, 'int4', Type::Integer
122
- register_class_with_limit m, 'int8', Type::Integer
123
- m.register_type 'oid', OID::Oid.new
124
- m.register_type 'float4', Type::Float.new
125
- m.alias_type 'float8', 'float4'
126
- m.register_type 'text', Type::Text.new
127
- register_class_with_limit m, 'varchar', Type::String
128
- m.alias_type 'char', 'varchar'
129
- m.alias_type 'name', 'varchar'
130
- m.alias_type 'bpchar', 'varchar'
131
- m.register_type 'bool', Type::Boolean.new
132
- register_class_with_limit m, 'bit', OID::Bit
133
- register_class_with_limit m, 'varbit', OID::BitVarying
134
- m.alias_type 'timestamptz', 'timestamp'
135
- m.register_type 'date', OID::Date.new
136
-
137
- m.register_type 'money', OID::Money.new
138
- m.register_type 'bytea', OID::Bytea.new
139
- m.register_type 'point', OID::Point.new
140
- m.register_type 'hstore', OID::Hstore.new
141
- m.register_type 'json', Type::Json.new
142
- m.register_type 'jsonb', OID::Jsonb.new
143
- m.register_type 'cidr', OID::Cidr.new
144
- m.register_type 'inet', OID::Inet.new
145
- m.register_type 'uuid', OID::Uuid.new
146
- m.register_type 'xml', OID::Xml.new
147
- m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
148
- m.register_type 'macaddr', OID::Macaddr.new
149
- m.register_type 'citext', OID::SpecializedString.new(:citext)
150
- m.register_type 'ltree', OID::SpecializedString.new(:ltree)
151
- m.register_type 'line', OID::SpecializedString.new(:line)
152
- m.register_type 'lseg', OID::SpecializedString.new(:lseg)
153
- m.register_type 'box', OID::SpecializedString.new(:box)
154
- m.register_type 'path', OID::SpecializedString.new(:path)
155
- m.register_type 'polygon', OID::SpecializedString.new(:polygon)
156
- m.register_type 'circle', OID::SpecializedString.new(:circle)
157
-
158
- m.register_type 'interval' do |*args, sql_type|
159
- precision = extract_precision(sql_type)
160
- OID::Interval.new(precision: precision)
161
- end
119
+ def register_class_with_limit(...)
120
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:register_class_with_limit, ...)
121
+ end
162
122
 
163
- register_class_with_precision m, 'time', Type::Time
164
- register_class_with_precision m, 'timestamp', OID::DateTime
123
+ def register_class_with_precision(...)
124
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:register_class_with_precision, ...)
125
+ end
165
126
 
166
- m.register_type 'numeric' do |_, fmod, sql_type|
127
+ def initialize_type_map(m = type_map)
128
+ m.register_type "int2", Type::Integer.new(limit: 2)
129
+ m.register_type "int4", Type::Integer.new(limit: 4)
130
+ m.register_type "int8", Type::Integer.new(limit: 8)
131
+ m.register_type "oid", OID::Oid.new
132
+ m.register_type "float4", Type::Float.new
133
+ m.alias_type "float8", "float4"
134
+ m.register_type "text", Type::Text.new
135
+ register_class_with_limit m, "varchar", Type::String
136
+ m.alias_type "char", "varchar"
137
+ m.alias_type "name", "varchar"
138
+ m.alias_type "bpchar", "varchar"
139
+ m.register_type "bool", Type::Boolean.new
140
+ register_class_with_limit m, "bit", OID::Bit
141
+ register_class_with_limit m, "varbit", OID::BitVarying
142
+ m.register_type "date", OID::Date.new
143
+
144
+ m.register_type "money", OID::Money.new
145
+ m.register_type "bytea", OID::Bytea.new
146
+ m.register_type "point", OID::Point.new
147
+ m.register_type "hstore", OID::Hstore.new
148
+ m.register_type "json", Type::Json.new
149
+ m.register_type "jsonb", OID::Jsonb.new
150
+ m.register_type "cidr", OID::Cidr.new
151
+ m.register_type "inet", OID::Inet.new
152
+ m.register_type "uuid", OID::Uuid.new
153
+ m.register_type "xml", OID::Xml.new
154
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
155
+ m.register_type "macaddr", OID::Macaddr.new
156
+ m.register_type "citext", OID::SpecializedString.new(:citext)
157
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
158
+ m.register_type "line", OID::SpecializedString.new(:line)
159
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
160
+ m.register_type "box", OID::SpecializedString.new(:box)
161
+ m.register_type "path", OID::SpecializedString.new(:path)
162
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
163
+ m.register_type "circle", OID::SpecializedString.new(:circle)
164
+
165
+ register_class_with_precision m, "time", Type::Time
166
+ register_class_with_precision m, "timestamp", OID::Timestamp
167
+ register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
168
+
169
+ m.register_type "numeric" do |_, fmod, sql_type|
167
170
  precision = extract_precision(sql_type)
168
171
  scale = extract_scale(sql_type)
169
172
 
@@ -183,7 +186,10 @@ module ArJdbc
183
186
  end
184
187
  end
185
188
 
186
- load_additional_types(m)
189
+ m.register_type "interval" do |*args, sql_type|
190
+ precision = extract_precision(sql_type)
191
+ OID::Interval.new(precision: precision)
192
+ end
187
193
 
188
194
  # pgjdbc returns these if the column is auto-incrmenting
189
195
  m.alias_type 'serial', 'int4'
@@ -231,6 +237,21 @@ module ArJdbc
231
237
  initializer.run(records)
232
238
  end
233
239
 
240
+ def extract_scale(sql_type)
241
+ case sql_type
242
+ when /\((\d+)\)/ then 0
243
+ when /\((\d+)(,(\d+))\)/ then $3.to_i
244
+ end
245
+ end
246
+
247
+ def extract_precision(sql_type)
248
+ $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
249
+ end
250
+
251
+ def extract_limit(sql_type)
252
+ $1.to_i if sql_type =~ /\((.*)\)/
253
+ end
254
+
234
255
  # Support arrays/ranges for defining attributes that don't exist in the db
235
256
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
236
257
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)