activerecord-jdbc-adapter 70.0-java → 70.1-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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/activerecord-jdbc-adapter.gemspec +1 -1
  4. data/lib/arel/visitors/compat.rb +5 -33
  5. data/lib/arel/visitors/h2.rb +1 -13
  6. data/lib/arel/visitors/hsqldb.rb +1 -21
  7. data/lib/arel/visitors/sql_server.rb +2 -103
  8. data/lib/arjdbc/abstract/database_statements.rb +8 -0
  9. data/lib/arjdbc/discover.rb +0 -67
  10. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  11. data/lib/arjdbc/jdbc/column.rb +1 -26
  12. data/lib/arjdbc/jdbc.rb +0 -7
  13. data/lib/arjdbc/oracle/adapter.rb +3 -22
  14. data/lib/arjdbc/postgresql/adapter.rb +152 -3
  15. data/lib/arjdbc/postgresql/oid_types.rb +155 -108
  16. data/lib/arjdbc/sqlite3/adapter.rb +27 -18
  17. data/lib/arjdbc/tasks/database_tasks.rb +0 -15
  18. data/lib/arjdbc/util/serialized_attributes.rb +0 -22
  19. data/lib/arjdbc/version.rb +1 -1
  20. data/rakelib/02-test.rake +3 -18
  21. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +5 -0
  22. metadata +2 -35
  23. data/lib/active_record/connection_adapters/as400_adapter.rb +0 -2
  24. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -1
  25. data/lib/active_record/connection_adapters/derby_adapter.rb +0 -1
  26. data/lib/active_record/connection_adapters/informix_adapter.rb +0 -1
  27. data/lib/arel/visitors/db2.rb +0 -137
  28. data/lib/arel/visitors/derby.rb +0 -112
  29. data/lib/arel/visitors/firebird.rb +0 -79
  30. data/lib/arjdbc/db2/adapter.rb +0 -808
  31. data/lib/arjdbc/db2/as400.rb +0 -142
  32. data/lib/arjdbc/db2/column.rb +0 -131
  33. data/lib/arjdbc/db2/connection_methods.rb +0 -48
  34. data/lib/arjdbc/db2.rb +0 -4
  35. data/lib/arjdbc/derby/active_record_patch.rb +0 -13
  36. data/lib/arjdbc/derby/adapter.rb +0 -521
  37. data/lib/arjdbc/derby/connection_methods.rb +0 -20
  38. data/lib/arjdbc/derby/schema_creation.rb +0 -15
  39. data/lib/arjdbc/derby.rb +0 -3
  40. data/lib/arjdbc/firebird/adapter.rb +0 -413
  41. data/lib/arjdbc/firebird/connection_methods.rb +0 -23
  42. data/lib/arjdbc/firebird.rb +0 -4
  43. data/lib/arjdbc/informix/adapter.rb +0 -139
  44. data/lib/arjdbc/informix/connection_methods.rb +0 -9
  45. data/lib/arjdbc/sybase/adapter.rb +0 -47
  46. data/lib/arjdbc/sybase.rb +0 -2
  47. data/lib/arjdbc/tasks/db2_database_tasks.rb +0 -104
  48. data/lib/arjdbc/tasks/derby_database_tasks.rb +0 -95
  49. data/src/java/arjdbc/derby/DerbyModule.java +0 -178
  50. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +0 -152
  51. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +0 -174
  52. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79e198f6d1579290fb0eeb3597282367757655bcff41b3e848a788b557573706
4
- data.tar.gz: 935a5c332bf06b2d77ca47ae6fc92128cbf450e1c50d64a293ef02ef2a04ad07
3
+ metadata.gz: '095c3d28051b253315cdd1bec95a83683f3240b0f3315cfb8f9c3c4c0d124f41'
4
+ data.tar.gz: 13b5d48e4c1b0b16811da5a361cfd9bb687727b5bbad2f5b750540b0232408ee
5
5
  SHA512:
6
- metadata.gz: b124009ca502a4e24c88a5fc1ff0a8a307e1850db491ae0cf12f0388753cccb575fff87f2421ec725dc9a945e335d2f6f8de1c7734ee6887f887d380e6726a68
7
- data.tar.gz: 82faa67a43955a2ff946510c3a77826365eb6dbd3adc7e7c81eecd83da03ce10ca37f0100a32b7089a3ef88014797a2960561851c701d695fe943f11f2712aae
6
+ metadata.gz: bc4801f970c8651e0ce8076b6544bf56d0e4decd77a9463439a8d5692f767aab896786680197c25b52d510465837290604970689b1b6cba26708385038f1fa39
7
+ data.tar.gz: 47e1df847914baefffadb80537b1ffd9dfcb218b2f5bfd26addefad934be7b31a0ade446c5838bea88b81897c34b13a7cef8e6b840540e89f4627f950949549c
data/Rakefile CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  require 'rake/clean'
28
28
 
29
- CLEAN.include 'derby*', 'test.db.*', '*test.sqlite3', 'test/reports'
29
+ CLEAN.include 'test.db.*', '*test.sqlite3', 'test/reports'
30
30
  CLEAN.include 'lib/**/*.jar', 'MANIFEST.MF', '*.log', 'target/*'
31
31
 
32
32
  task :default => :jar # RubyGems extention will do a bare `rake' e.g. :
@@ -47,6 +47,6 @@ Gem::Specification.new do |gem|
47
47
  #gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
48
48
  #gem.add_development_dependency 'mocha', '~> 0.13.1'
49
49
 
50
- gem.rdoc_options = ["--main", "README.md", "-SHN", "-f", "darkfish"]
50
+ gem.rdoc_options = ["--main", "README.md"]
51
51
  end
52
52
 
@@ -4,44 +4,16 @@ module Arel
4
4
 
5
5
  protected
6
6
 
7
- if ToSql.instance_method('visit').arity == 1
8
- def do_visit(x, a); visit(x); end # a = nil
9
- else # > AREL 4.0
10
- def do_visit(x, a); visit(x, a); end
11
- end
7
+ def do_visit(x, a); visit(x, a); end
12
8
 
13
- if ToSql.instance_method('visit_Arel_Nodes_SelectCore').arity == 1
14
- def do_visit_select_core(x, a) # a = nil
15
- visit_Arel_Nodes_SelectCore(x)
16
- end
17
- else # > AREL 4.0
18
- def do_visit_select_core(x, a)
19
- visit_Arel_Nodes_SelectCore(x, a)
20
- end
9
+ def do_visit_select_core(x, a)
10
+ visit_Arel_Nodes_SelectCore(x, a)
21
11
  end
22
12
 
23
13
  private
24
14
 
25
- if ArJdbc::AR42
26
- if Arel::VERSION < '6.0.2'
27
- def limit_for(limit_or_node)
28
- if limit_or_node.respond_to?(:expr)
29
- expr = limit_or_node.expr
30
- # NOTE(uwe): Different behavior for Arel 6.0.0 and 6.0.2
31
- expr.respond_to?(:value) ? expr.value.to_i : expr.to_i
32
- else
33
- limit_or_node
34
- end
35
- end
36
- else
37
- def limit_for(limit_or_node)
38
- limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
39
- end
40
- end
41
- else
42
- def limit_for(limit_or_node)
43
- limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
44
- end
15
+ def limit_for(limit_or_node)
16
+ limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
45
17
  end
46
18
  module_function :limit_for
47
19
 
@@ -7,19 +7,7 @@ module Arel
7
7
  def visit_Arel_Nodes_SelectStatement(o, *)
8
8
  o.limit ||= Arel::Nodes::Limit.new(-1) if o.offset
9
9
  super
10
- end if ArJdbc::AR42
11
-
12
- def limit_offset sql, o
13
- offset = o.offset || 0
14
- offset = offset.expr unless (offset.nil? || offset == 0)
15
- if limit = o.limit
16
- "SELECT LIMIT #{offset} #{limit_for(limit)} #{sql[7..-1]}"
17
- elsif offset > 0
18
- "SELECT LIMIT #{offset} -1 #{sql[7..-1]}" # removes "SELECT "
19
- else
20
- sql
21
- end
22
- end unless ArJdbc::AR42
10
+ end
23
11
  end
24
12
  end
25
13
  end
@@ -6,27 +6,7 @@ module Arel
6
6
  def visit_Arel_Nodes_SelectStatement(o, *)
7
7
  o.limit ||= Arel::Nodes::Limit.new(0) if o.offset
8
8
  super
9
- end if ArJdbc::AR42
10
-
11
- def visit_Arel_Nodes_SelectStatement o, a = nil
12
- sql = limit_offset(o.cores.map { |x| do_visit_select_core x, a }.join, o)
13
- sql << " ORDER BY #{o.orders.map { |x| do_visit x, a }.join(', ')}" unless o.orders.empty?
14
- sql
15
- end unless ArJdbc::AR42
16
-
17
- private
18
-
19
- def limit_offset sql, o
20
- offset = o.offset || 0
21
- offset = offset.expr unless (offset.nil? || offset == 0)
22
- if limit = o.limit
23
- "SELECT LIMIT #{offset} #{limit_for(limit)} #{sql[7..-1]}"
24
- elsif offset > 0
25
- "SELECT LIMIT #{offset} 0 #{sql[7..-1]}" # removes "SELECT "
26
- else
27
- sql
28
- end
29
- end unless ArJdbc::AR42
9
+ end
30
10
  end
31
11
  end
32
12
  end
@@ -11,44 +11,6 @@ module Arel
11
11
 
12
12
  private
13
13
 
14
- def visit_Arel_Nodes_SelectStatement(*args) # [o] AR <= 4.0 [o, a] on 4.1
15
- o, a = args.first, args.last
16
-
17
- return _visit_Arel_Nodes_SelectStatement(*args) if ! o.limit && ! o.offset
18
-
19
- unless o.orders.empty?
20
- select_order_by = do_visit_columns o.orders, a, 'ORDER BY '
21
- end
22
-
23
- select_count = false; sql = ''
24
- o.cores.each do |x|
25
- x = x.dup
26
- core_order_by = select_order_by || determine_order_by(x, a)
27
- if select_count? x
28
- x.projections = [
29
- Arel::Nodes::SqlLiteral.new(core_order_by ? over_row_num(core_order_by) : '*')
30
- ]
31
- select_count = true
32
- else
33
- # NOTE: this should really be added here and we should built the
34
- # wrapping SQL but than #replace_limit_offset! assumes it does that
35
- # ... MS-SQL adapter code seems to be 'hacked' by a lot of people
36
- #x.projections << Arel::Nodes::SqlLiteral.new over_row_num(order_by)
37
- end
38
- sql << do_visit_select_core(x, a)
39
- end
40
-
41
- #sql = "SELECT _t.* FROM (#{sql}) as _t WHERE #{get_offset_limit_clause(o)}"
42
- select_order_by ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
43
- replace_limit_offset!(sql, limit_for(o.limit), o.offset && o.offset.value.to_i, select_order_by)
44
-
45
- sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if select_count
46
-
47
- add_lock!(sql, :lock => o.lock && true)
48
-
49
- sql
50
- end unless ArJdbc::AR42
51
-
52
14
  # @private
53
15
  MAX_LIMIT_VALUE = 9_223_372_036_854_775_807
54
16
 
@@ -60,14 +22,6 @@ module Arel
60
22
  super
61
23
  end
62
24
 
63
- def visit_Arel_Nodes_Lock o, a = nil
64
- # MS-SQL doesn't support "SELECT...FOR UPDATE". Instead, it needs
65
- # WITH(ROWLOCK,UPDLOCK) specified after each table in the FROM clause.
66
- #
67
- # we return nothing here and add the appropriate stuff with #add_lock!
68
- #do_visit o.expr, a
69
- end unless ArJdbc::AR42
70
-
71
25
  def visit_Arel_Nodes_Top o, a = nil
72
26
  # `top` wouldn't really work here:
73
27
  # User.select("distinct first_name").limit(10)
@@ -76,29 +30,6 @@ module Arel
76
30
  a || ''
77
31
  end
78
32
 
79
- def visit_Arel_Nodes_Limit o, a = nil
80
- "TOP (#{do_visit o.expr, a})"
81
- end unless ArJdbc::AR42
82
-
83
- def visit_Arel_Nodes_Ordering o, a = nil
84
- expr = do_visit o.expr, a
85
- if o.respond_to?(:direction)
86
- "#{expr} #{o.ascending? ? 'ASC' : 'DESC'}"
87
- else
88
- expr
89
- end
90
- end unless ArJdbc::AR42
91
-
92
- def visit_Arel_Nodes_Bin o, a = nil
93
- expr = o.expr; sql = do_visit expr, a
94
- if expr.respond_to?(:val) && expr.val.is_a?(Numeric)
95
- sql
96
- else
97
- sql << " #{::ArJdbc::MSSQL.cs_equality_operator} "
98
- sql
99
- end
100
- end unless ArJdbc::AR42
101
-
102
33
  private
103
34
 
104
35
  def self.possibly_private_method_defined?(name)
@@ -140,25 +71,7 @@ module Arel
140
71
  visit(x, sql); sql << ', ' unless i == last
141
72
  end
142
73
  sql.value
143
- end if ArJdbc::AR42
144
-
145
- def do_visit_columns(colls, a, sql)
146
- non_simple_order = /\sASC|\sDESC|\sCASE|\sCOLLATE|[\.,\[\(]/i # MIN(width)
147
-
148
- last = colls.size - 1
149
- colls.each_with_index do |x, i|
150
- coll = do_visit(x, a)
151
-
152
- if coll !~ non_simple_order && coll.to_i == 0
153
- sql << @connection.quote_column_name(coll)
154
- else
155
- sql << coll
156
- end
157
-
158
- sql << ', ' unless i == last
159
- end
160
- sql
161
- end if Arel::VERSION < '4.0.0'
74
+ end
162
75
 
163
76
  def over_row_num order_by
164
77
  "ROW_NUMBER() OVER (#{order_by}) as _row_num"
@@ -174,20 +87,6 @@ module Arel
174
87
  end
175
88
  end
176
89
 
177
- def table_from_select_core core
178
- table_finder = lambda do |x|
179
- case x
180
- when Arel::Table
181
- x
182
- when Arel::Nodes::SqlLiteral
183
- Arel::Table.new(x, @engine)
184
- when Arel::Nodes::Join
185
- table_finder.call(x.left)
186
- end
187
- end
188
- table_finder.call(core.froms)
189
- end if ActiveRecord::VERSION::STRING < '3.2'
190
-
191
90
  def primary_key_from_table t
192
91
  return unless t
193
92
  return t.primary_key if t.primary_key
@@ -219,7 +118,7 @@ module Arel
219
118
  include ArJdbc::MSSQL::LimitHelpers::SqlServer2000ReplaceLimitOffset
220
119
  end
221
120
 
222
- load 'arel/visitors/sql_server/ng42.rb' if ArJdbc::AR42
121
+ load 'arel/visitors/sql_server/ng42.rb'
223
122
 
224
123
  end
225
124
  end
@@ -10,6 +10,8 @@ module ArJdbc
10
10
  NO_BINDS = [].freeze
11
11
 
12
12
  def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil)
13
+ sql = transform_query(sql)
14
+
13
15
  if preventing_writes?
14
16
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
15
17
  end
@@ -31,6 +33,8 @@ module ArJdbc
31
33
  # It appears that at this point (AR 5.0) "prepare" should only ever be true
32
34
  # if prepared statements are enabled
33
35
  def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
36
+ sql = transform_query(sql)
37
+
34
38
  if preventing_writes? && write_query?(sql)
35
39
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
36
40
  end
@@ -52,6 +56,8 @@ module ArJdbc
52
56
  end
53
57
 
54
58
  def exec_update(sql, name = nil, binds = NO_BINDS)
59
+ sql = transform_query(sql)
60
+
55
61
  if preventing_writes?
56
62
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
57
63
  end
@@ -70,6 +76,8 @@ module ArJdbc
70
76
  alias :exec_delete :exec_update
71
77
 
72
78
  def execute(sql, name = nil, async: false)
79
+ sql = transform_query(sql)
80
+
73
81
  if preventing_writes? && write_query?(sql)
74
82
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
75
83
  end
@@ -23,26 +23,6 @@ module ArJdbc
23
23
  require('arjdbc/sqlite3') || true if name =~ /sqlite/i
24
24
  end
25
25
 
26
- # Other supported adapters :
27
-
28
- extension :Derby do |name, config|
29
- if name =~ /derby/i
30
- require 'arjdbc/derby'
31
-
32
- if config && config[:username].nil? # set the database schema name (:username) :
33
- begin
34
- ArJdbc.with_meta_data_from_data_source_if_any(config) do
35
- |meta_data| config[:username] = meta_data.getUserName
36
- end
37
- rescue => e
38
- ArJdbc.warn("failed to set :username from (Derby) database meda-data: #{e.inspect}")
39
- end
40
- end
41
-
42
- true
43
- end
44
- end
45
-
46
26
  extension :H2 do |name|
47
27
  require('arjdbc/h2') || true if name =~ /\.h2\./i
48
28
  end
@@ -54,51 +34,4 @@ module ArJdbc
54
34
  extension :MSSQL do |name|
55
35
  require('arjdbc/mssql') || true if name =~ /sqlserver|tds|Microsoft SQL/i
56
36
  end
57
-
58
- extension :DB2 do |name, config|
59
- if name =~ /db2/i && name !~ /as\/?400/i && config[:url] !~ /^jdbc:derby:net:/
60
- require 'arjdbc/db2'
61
- true
62
- end
63
- end
64
-
65
- extension :AS400 do |name, config|
66
- # The native JDBC driver always returns "DB2 UDB for AS/400"
67
- if name =~ /as\/?400/i
68
- require 'arjdbc/db2'
69
- require 'arjdbc/db2/as400'
70
- true
71
- end
72
- end
73
-
74
- # NOTE: following ones are likely getting deprecated :
75
-
76
- extension :FireBird do |name|
77
- if name =~ /firebird/i
78
- require 'arjdbc/firebird'
79
- true
80
- end
81
- end
82
-
83
- extension :Sybase do |name|
84
- if name =~ /sybase|tds/i
85
- require 'arjdbc/sybase'
86
- true
87
- end
88
- end
89
-
90
- extension :Informix do |name|
91
- if name =~ /informix/i
92
- require 'arjdbc/informix'
93
- true
94
- end
95
- end
96
-
97
- extension :Mimer do |name|
98
- if name =~ /mimer/i
99
- require 'arjdbc/mimer'
100
- true
101
- end
102
- end
103
-
104
37
  end
Binary file
@@ -28,21 +28,6 @@ module ActiveRecord
28
28
  end
29
29
  end
30
30
 
31
- if ArJdbc::AR52
32
- # undefined method `cast' for #<ActiveRecord::ConnectionAdapters::SqlTypeMetadata> on AR52
33
- else
34
- default = args[0].cast(default)
35
-
36
- sql_type = args.delete_at(1)
37
- type = args.delete_at(0)
38
-
39
- args.unshift(SqlTypeMetadata.new(:sql_type => sql_type, :type => type))
40
- end
41
-
42
- # super <= 4.1: (name, default, sql_type = nil, null = true)
43
- # super >= 4.2: (name, default, cast_type, sql_type = nil, null = true)
44
- # super >= 5.0: (name, default, sql_type_metadata = nil, null = true)
45
-
46
31
  super(name, default, *args)
47
32
  init_column(name, default, *args)
48
33
  end
@@ -80,18 +65,8 @@ module ActiveRecord
80
65
  end
81
66
 
82
67
  class << self
83
-
84
- include Jdbc::TypeCast if ::ActiveRecord::VERSION::STRING >= '4.2'
85
-
86
- if ActiveRecord::VERSION::MAJOR > 3 && ActiveRecord::VERSION::STRING < '4.2'
87
-
88
- # @private provides compatibility between AR 3.x/4.0 API
89
- def string_to_date(value); value_to_date(value) end
90
-
91
- end
92
-
68
+ include Jdbc::TypeCast
93
69
  end
94
-
95
70
  end
96
71
  end
97
72
  end
data/lib/arjdbc/jdbc.rb CHANGED
@@ -1,13 +1,6 @@
1
1
  require 'active_support/deprecation'
2
2
 
3
3
  module ArJdbc
4
-
5
- # @private
6
- AR42 = ::ActiveRecord::VERSION::STRING >= '4.2'
7
-
8
- # @private
9
- AR52 = ::ActiveRecord::VERSION::STRING >= '5.2'
10
-
11
4
  class << self
12
5
 
13
6
  # @private Internal API
@@ -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
@@ -21,6 +21,7 @@ require 'arjdbc/abstract/transaction_support'
21
21
  require 'arjdbc/postgresql/base/array_decoder'
22
22
  require 'arjdbc/postgresql/base/array_encoder'
23
23
  require 'arjdbc/postgresql/name'
24
+ require 'active_model'
24
25
 
25
26
  module ArJdbc
26
27
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
@@ -320,6 +321,38 @@ module ArJdbc
320
321
  exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
321
322
  end
322
323
 
324
+ # Returns a list of defined enum types, and their values.
325
+ def enum_types
326
+ query = <<~SQL
327
+ SELECT
328
+ type.typname AS name,
329
+ string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
330
+ FROM pg_enum AS enum
331
+ JOIN pg_type AS type
332
+ ON (type.oid = enum.enumtypid)
333
+ GROUP BY type.typname;
334
+ SQL
335
+ exec_query(query, "SCHEMA").cast_values
336
+ end
337
+
338
+ # Given a name and an array of values, creates an enum type.
339
+ def create_enum(name, values)
340
+ sql_values = values.map { |s| "'#{s}'" }.join(", ")
341
+ query = <<~SQL
342
+ DO $$
343
+ BEGIN
344
+ IF NOT EXISTS (
345
+ SELECT 1 FROM pg_type t
346
+ WHERE t.typname = '#{name}'
347
+ ) THEN
348
+ CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
349
+ END IF;
350
+ END
351
+ $$;
352
+ SQL
353
+ exec_query(query)
354
+ end
355
+
323
356
  # Returns the configured supported identifier length supported by PostgreSQL
324
357
  def max_identifier_length
325
358
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
@@ -672,6 +705,37 @@ module ActiveRecord::ConnectionAdapters
672
705
  class PostgreSQLAdapter < AbstractAdapter
673
706
  class_attribute :create_unlogged_tables, default: false
674
707
 
708
+ ##
709
+ # :singleton-method:
710
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
711
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
712
+ # but significantly increases the risk of data loss if the database
713
+ # crashes. As a result, this should not be used in production
714
+ # environments. If you would like all created tables to be unlogged in
715
+ # the test environment you can add the following line to your test.rb
716
+ # file:
717
+ #
718
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
719
+ class_attribute :create_unlogged_tables, default: false
720
+
721
+ ##
722
+ # :singleton-method:
723
+ # PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
724
+ # in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
725
+ # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
726
+ # store DateTimes as "timestamp with time zone":
727
+ #
728
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
729
+ #
730
+ # Or if you are adding a custom type:
731
+ #
732
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
733
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
734
+ #
735
+ # If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
736
+ # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
737
+ class_attribute :datetime_type, default: :timestamp
738
+
675
739
  # Try to use as much of the built in postgres logic as possible
676
740
  # maybe someday we can extend the actual adapter
677
741
  include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
@@ -735,11 +799,96 @@ module ActiveRecord::ConnectionAdapters
735
799
 
736
800
  private
737
801
 
738
- # Prepared statements aren't schema aware so we need to make sure we
739
- # store different PreparedStatement objects for different schemas
802
+ FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
803
+
804
+ def execute_and_clear(sql, name, binds, prepare: false, async: false)
805
+ sql = transform_query(sql)
806
+ check_if_write_query(sql)
807
+
808
+ if !prepare || without_prepared_statement?(binds)
809
+ result = exec_no_cache(sql, name, binds, async: async)
810
+ else
811
+ result = exec_cache(sql, name, binds, async: async)
812
+ end
813
+ begin
814
+ ret = yield result
815
+ ensure
816
+ # Is this really result in AR PG?
817
+ # result.clear
818
+ end
819
+ ret
820
+ end
821
+
822
+ def exec_no_cache(sql, name, binds, async: false)
823
+ materialize_transactions
824
+ mark_transaction_written_if_write(sql)
825
+
826
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
827
+ # made since we established the connection
828
+ update_typemap_for_default_timezone
829
+
830
+ type_casted_binds = type_casted_binds(binds)
831
+ log(sql, name, binds, type_casted_binds, async: async) do
832
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
833
+ @connection.exec_params(sql, type_casted_binds)
834
+ end
835
+ end
836
+ end
837
+
838
+ def exec_cache(sql, name, binds, async: false)
839
+ materialize_transactions
840
+ mark_transaction_written_if_write(sql)
841
+ update_typemap_for_default_timezone
842
+
843
+ stmt_key = prepare_statement(sql, binds)
844
+ type_casted_binds = type_casted_binds(binds)
845
+
846
+ log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
847
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
848
+ @connection.exec_prepared(stmt_key, type_casted_binds)
849
+ end
850
+ end
851
+ rescue ActiveRecord::StatementInvalid => e
852
+ raise unless is_cached_plan_failure?(e)
853
+
854
+ # Nothing we can do if we are in a transaction because all commands
855
+ # will raise InFailedSQLTransaction
856
+ if in_transaction?
857
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
858
+ else
859
+ @lock.synchronize do
860
+ # outside of transactions we can simply flush this query and retry
861
+ @statements.delete sql_key(sql)
862
+ end
863
+ retry
864
+ end
865
+ end
866
+
867
+ # Annoyingly, the code for prepared statements whose return value may
868
+ # have changed is FEATURE_NOT_SUPPORTED.
869
+ #
870
+ # This covers various different error types so we need to do additional
871
+ # work to classify the exception definitively as a
872
+ # ActiveRecord::PreparedStatementCacheExpired
873
+ #
874
+ # Check here for more details:
875
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
876
+ def is_cached_plan_failure?(e)
877
+ pgerror = e.cause
878
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
879
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
880
+ rescue
881
+ false
882
+ end
883
+
884
+ def in_transaction?
885
+ open_transactions > 0
886
+ end
887
+
888
+ # Returns the statement identifier for the client side cache
889
+ # of statements
740
890
  def sql_key(sql)
741
891
  "#{schema_search_path}-#{sql}"
742
892
  end
743
-
744
893
  end
745
894
  end