activerecord-yugabytedb-adapter 7.0.4.1 → 7.1.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3db689c384694018413781b6b103d41fe67fa1f01e9e26e7e628b4bb2bb07535
4
- data.tar.gz: '008122cddcd6ff3d30c3527c67d4455f91298a7822242f8de51d4abc8add98eb'
3
+ metadata.gz: f2cc03a2de664cf174a7f51d10d8a7d466a1746a2a664e65c84a063b1690c176
4
+ data.tar.gz: 81ca6a9131426b34378264ffac4495387b1351c5c23cf476ab32334f07676696
5
5
  SHA512:
6
- metadata.gz: c23aaf43a62c4a84848678204dd9cf54184bff185e0998955a0c13e6c7bd2cd84ba1c966567a31236678abe128cd91937c3fcbe51c55c8cbc866bfff2837dc11
7
- data.tar.gz: f87027546cb7cdb1cb732bc0b880eda9eb20e599f87af2f4bd3e21f51d769503bc820ac4e99c7c44e255f8aea0dd60e686b827f17a5c0f974f460e69969f1a9e
6
+ metadata.gz: 0ffc610ae76a0d049e8bd35eeea4b5c7ef557c5774768be0b7d28c78da446893813fd2342b339d91b11363ccd3fbfcefda0514b7ed1976d87088ec85b12a82e8
7
+ data.tar.gz: d2a122f7b7af63ab3434ae6dfbfa7d05993a6f3aeb7fc6ae6149196857f7125aa3b712babd67ce18b963076ea52ae9362c93fd8fa2f7a7a083c1dc66e8f0f294
data/Gemfile.lock CHANGED
@@ -2,29 +2,40 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  activerecord-yugabytedb-adapter (7.0.4.1)
5
- activerecord (= 7.0.4)
6
- yugabyte_ysql (~> 0.3)
5
+ activerecord (= 7.1.3.4)
6
+ yugabytedb-ysql (~> 0.3)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (7.0.4)
12
- activesupport (= 7.0.4)
13
- activerecord (7.0.4)
14
- activemodel (= 7.0.4)
15
- activesupport (= 7.0.4)
16
- activesupport (7.0.4)
11
+ activemodel (7.1.3.4)
12
+ activesupport (= 7.1.3.4)
13
+ activerecord (7.1.3.4)
14
+ activemodel (= 7.1.3.4)
15
+ activesupport (= 7.1.3.4)
16
+ timeout (>= 0.4.0)
17
+ activesupport (7.1.3.4)
18
+ base64
19
+ bigdecimal
17
20
  concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ connection_pool (>= 2.2.5)
22
+ drb
18
23
  i18n (>= 1.6, < 2)
19
24
  minitest (>= 5.1)
25
+ mutex_m
20
26
  tzinfo (~> 2.0)
21
27
  ast (2.4.2)
28
+ base64 (0.2.0)
29
+ bigdecimal (3.1.8)
22
30
  concurrent-ruby (1.3.3)
31
+ connection_pool (2.4.1)
32
+ drb (2.2.1)
23
33
  i18n (1.14.5)
24
34
  concurrent-ruby (~> 1.0)
25
35
  json (2.7.2)
26
36
  language_server-protocol (3.17.0.3)
27
37
  minitest (5.24.0)
38
+ mutex_m (0.2.0)
28
39
  parallel (1.25.1)
29
40
  parser (3.3.3.0)
30
41
  ast (~> 2.4.1)
@@ -50,10 +61,11 @@ GEM
50
61
  parser (>= 3.3.1.0)
51
62
  ruby-progressbar (1.13.0)
52
63
  strscan (3.1.0)
64
+ timeout (0.4.1)
53
65
  tzinfo (2.0.6)
54
66
  concurrent-ruby (~> 1.0)
55
67
  unicode-display_width (2.5.0)
56
- yugabyte_ysql (0.3)
68
+ yugabytedb-ysql (0.3)
57
69
 
58
70
  PLATFORMS
59
71
  x86_64-linux
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ActiveRecord YugabyteDB Adapter
2
2
 
3
- This is an adapter for YugabyteDB for ActiveRecord. The adapter uses YugabyteDB's Ruby smart driver underneath. The adapter code is derived from the [PostgreSQL adapter v7.0.4 for ActiveRecord in the Rails repository](https://github.com/rails/rails/tree/v7.0.4/activerecord/lib/active_record/connection_adapters).
3
+ This is an adapter for YugabyteDB for ActiveRecord. The adapter uses YugabyteDB's Ruby smart driver underneath. The adapter code is derived from the [PostgreSQL adapter v7.1.3.4 for ActiveRecord in the Rails repository](https://github.com/rails/rails/tree/v7.1.3.4/activerecord/lib/active_record/connection_adapters).
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,7 +14,13 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
14
 
15
15
  ## Usage
16
16
 
17
- Check this [simple example](https://github.com/YugabyteDB-Samples/orm-examples/tree/ruby-smart-driver) for usage of this adapter.
17
+ Include the gem in your `Gemfile`
18
+ ```ruby
19
+ gem 'rails', '7.1.3.4'
20
+ gem 'activerecord-yugabytedb-adapter', '7.1.3.4'
21
+ ```
22
+
23
+ Check this [simple example](https://github.com/YugabyteDB-Samples/orm-examples/tree/ruby-smart-driver/ruby/ror) for usage of this adapter.
18
24
 
19
25
  ## Development
20
26
 
@@ -30,8 +30,8 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
32
  # spec.add_dependency "example-gem", "~> 1.0"
33
- spec.add_dependency "activerecord", "7.0.4"
34
- spec.add_dependency "yugabyte_ysql", "~> 0.3"
33
+ spec.add_dependency "activerecord", "7.1.3.4"
34
+ spec.add_dependency "yugabytedb-ysql", "~> 0.3"
35
35
 
36
36
 
37
37
  # For more information and examples about making a new gem, check out our
@@ -1,23 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/object/blank"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters
7
5
  module YugabyteDB
8
6
  class Column < ConnectionAdapters::Column # :nodoc:
9
7
  delegate :oid, :fmod, to: :sql_type_metadata
10
8
 
11
- def initialize(*, serial: nil, generated: nil, **)
9
+ def initialize(*, serial: nil, identity: nil, generated: nil, **)
12
10
  super
13
11
  @serial = serial
12
+ @identity = identity
14
13
  @generated = generated
15
14
  end
16
15
 
16
+ def identity?
17
+ @identity
18
+ end
19
+
17
20
  def serial?
18
21
  @serial
19
22
  end
20
23
 
24
+ def auto_incremented_by_db?
25
+ serial? || identity?
26
+ end
27
+
21
28
  def virtual?
22
29
  # We assume every generated column is virtual, no matter the concrete type
23
30
  @generated.present?
@@ -42,17 +49,22 @@ module ActiveRecord
42
49
 
43
50
  def init_with(coder)
44
51
  @serial = coder["serial"]
52
+ @identity = coder["identity"]
53
+ @generated = coder["generated"]
45
54
  super
46
55
  end
47
56
 
48
57
  def encode_with(coder)
49
58
  coder["serial"] = @serial
59
+ coder["identity"] = @identity
60
+ coder["generated"] = @generated
50
61
  super
51
62
  end
52
63
 
53
64
  def ==(other)
54
65
  other.is_a?(Column) &&
55
66
  super &&
67
+ identity? == other.identity? &&
56
68
  serial? == other.serial?
57
69
  end
58
70
  alias :eql? :==
@@ -60,6 +72,7 @@ module ActiveRecord
60
72
  def hash
61
73
  Column.hash ^
62
74
  super.hash ^
75
+ identity?.hash ^
63
76
  serial?.hash
64
77
  end
65
78
  end
@@ -4,19 +4,21 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module YugabyteDB
6
6
  module DatabaseStatements
7
- def explain(arel, binds = [])
8
- sql = "EXPLAIN #{to_sql(arel, binds)}"
9
- YugabyteDB::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
7
+ def explain(arel, binds = [], options = [])
8
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
9
+ result = internal_exec_query(sql, "EXPLAIN", binds)
10
+ YugabyteDB::ExplainPrettyPrinter.new.pp(result)
10
11
  end
11
12
 
12
13
  # Queries the database and returns the results in an Array-like object
13
14
  def query(sql, name = nil) # :nodoc:
14
- materialize_transactions
15
15
  mark_transaction_written_if_write(sql)
16
16
 
17
17
  log(sql, name) do
18
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
19
- @connection.async_exec(sql).map_types!(@type_map_for_results).values
18
+ with_raw_connection do |conn|
19
+ result = conn.async_exec(sql).map_types!(@type_map_for_results).values
20
+ verified!
21
+ result
20
22
  end
21
23
  end
22
24
  end
@@ -34,34 +36,38 @@ module ActiveRecord
34
36
 
35
37
  # Executes an SQL statement, returning a PG::Result object on success
36
38
  # or raising a PG::Error exception otherwise.
39
+ #
40
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
41
+ # executing the SQL statement in case of a connection-related exception.
42
+ # This option should only be enabled for known idempotent queries.
43
+ #
37
44
  # Note: the PG::Result object is manually memory managed; if you don't
38
45
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
39
- def execute(sql, name = nil)
40
- sql = transform_query(sql)
41
- check_if_write_query(sql)
42
-
43
- materialize_transactions
44
- mark_transaction_written_if_write(sql)
46
+ def execute(...) # :nodoc:
47
+ super
48
+ ensure
49
+ @notice_receiver_sql_warnings = []
50
+ end
45
51
 
46
- log(sql, name) do
47
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
48
- @connection.async_exec(sql)
52
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
53
+ log(sql, name, async: async) do
54
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
55
+ result = conn.async_exec(sql)
56
+ verified!
57
+ handle_warnings(result)
58
+ result
49
59
  end
50
60
  end
51
61
  end
52
62
 
53
- def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
54
- execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
63
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
64
+ execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
55
65
  types = {}
56
66
  fields = result.fields
57
67
  fields.each_with_index do |fname, i|
58
68
  ftype = result.ftype i
59
69
  fmod = result.fmod i
60
- case type = get_oid_type(ftype, fmod, fname)
61
- when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
62
- # skip if a column has already been type casted by pg decoders
63
- else types[fname] = type
64
- end
70
+ types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
65
71
  end
66
72
  build_result(columns: fields, rows: result.values, column_types: types)
67
73
  end
@@ -72,26 +78,11 @@ module ActiveRecord
72
78
  end
73
79
  alias :exec_update :exec_delete
74
80
 
75
- def sql_for_insert(sql, pk, binds) # :nodoc:
76
- if pk.nil?
77
- # Extract the table from the insert sql. Yuck.
78
- table_ref = extract_table_ref_from_insert_sql(sql)
79
- pk = primary_key(table_ref) if table_ref
80
- end
81
-
82
- if pk = suppress_composite_primary_key(pk)
83
- sql = "#{sql} RETURNING #{quote_column_name(pk)}"
84
- end
85
-
86
- super
87
- end
88
- private :sql_for_insert
89
-
90
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
81
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
91
82
  if use_insert_returning? || pk == false
92
83
  super
93
84
  else
94
- result = exec_query(sql, name, binds)
85
+ result = internal_exec_query(sql, name, binds)
95
86
  unless sequence_name
96
87
  table_ref = extract_table_ref_from_insert_sql(sql)
97
88
  if table_ref
@@ -107,22 +98,27 @@ module ActiveRecord
107
98
 
108
99
  # Begins a transaction.
109
100
  def begin_db_transaction # :nodoc:
110
- execute("BEGIN", "TRANSACTION")
101
+ internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
111
102
  end
112
103
 
113
104
  def begin_isolated_db_transaction(isolation) # :nodoc:
114
- begin_db_transaction
115
- execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
105
+ internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
116
106
  end
117
107
 
118
108
  # Commits a transaction.
119
109
  def commit_db_transaction # :nodoc:
120
- execute("COMMIT", "TRANSACTION")
110
+ internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
121
111
  end
122
112
 
123
113
  # Aborts a transaction.
124
114
  def exec_rollback_db_transaction # :nodoc:
125
- execute("ROLLBACK", "TRANSACTION")
115
+ cancel_any_running_query
116
+ internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
117
+ end
118
+
119
+ def exec_restart_db_transaction # :nodoc:
120
+ cancel_any_running_query
121
+ internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
126
122
  end
127
123
 
128
124
  # From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
@@ -133,7 +129,24 @@ module ActiveRecord
133
129
  HIGH_PRECISION_CURRENT_TIMESTAMP
134
130
  end
135
131
 
132
+ def build_explain_clause(options = [])
133
+ return "EXPLAIN" if options.empty?
134
+
135
+ "EXPLAIN (#{options.join(", ").upcase})"
136
+ end
137
+
136
138
  private
139
+ IDLE_TRANSACTION_STATUSES = [YSQL::PQTRANS_IDLE, YSQL::PQTRANS_INTRANS, YSQL::PQTRANS_INERROR]
140
+ private_constant :IDLE_TRANSACTION_STATUSES
141
+
142
+ def cancel_any_running_query
143
+ return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
144
+
145
+ @raw_connection.cancel
146
+ @raw_connection.block
147
+ rescue YSQL::Error
148
+ end
149
+
137
150
  def execute_batch(statements, name = nil)
138
151
  execute(combine_multi_statements(statements))
139
152
  end
@@ -144,12 +157,29 @@ module ActiveRecord
144
157
 
145
158
  # Returns the current ID of a table's sequence.
146
159
  def last_insert_id_result(sequence_name)
147
- exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
160
+ internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
161
+ end
162
+
163
+ def returning_column_values(result)
164
+ result.rows.first
148
165
  end
149
166
 
150
167
  def suppress_composite_primary_key(pk)
151
168
  pk unless pk.is_a?(Array)
152
169
  end
170
+
171
+ def handle_warnings(sql)
172
+ @notice_receiver_sql_warnings.each do |warning|
173
+ next if warning_ignored?(warning)
174
+
175
+ warning.sql = sql
176
+ ActiveRecord.db_warnings_action.call(warning)
177
+ end
178
+ end
179
+
180
+ def warning_ignored?(warning)
181
+ ["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
182
+ end
153
183
  end
154
184
  end
155
185
  end
@@ -16,8 +16,8 @@ module ActiveRecord
16
16
  @subtype = subtype
17
17
  @delimiter = delimiter
18
18
 
19
- @pg_encoder = YugabyteYSQL::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
20
- @pg_decoder = YugabyteYSQL::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
19
+ @pg_encoder = YSQL::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
20
+ @pg_decoder = YSQL::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
21
21
  end
22
22
 
23
23
  def deserialize(value)
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  end
66
66
 
67
67
  def map(value, &block)
68
- value.map(&block)
68
+ value.map { |v| subtype.map(v, &block) }
69
69
  end
70
70
 
71
71
  def changed_in_place?(raw_old_value, new_value)
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  def deserialize(value)
9
9
  return if value.nil?
10
10
  return value.to_s if value.is_a?(Type::Binary::Data)
11
- YugabyteYSQL::Connection.unescape_bytea(super)
11
+ YSQL::Connection.unescape_bytea(super)
12
12
  end
13
13
  end
14
14
  end
@@ -27,9 +27,10 @@ module ActiveRecord
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
29
  when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
30
- value.gsub!(/[^-\d.]/, "")
30
+ value.delete!("^-0-9.")
31
31
  when /^-?\D*+[\d.]+,\d{2}$/ # (2)
32
- value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
32
+ value.delete!("^-0-9,")
33
+ value.tr!(",", ".")
33
34
  end
34
35
 
35
36
  super(value)
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def cast_value(value)
21
- return if value == "empty"
21
+ return if ["empty", ""].include? value
22
22
  return value unless value.is_a?(::String)
23
23
 
24
24
  extracted = extract_bounds(value)
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  if !infinity?(from) && extracted[:exclude_start]
29
29
  raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
30
30
  end
31
- ::Range.new(from, to, extracted[:exclude_end])
31
+ ::Range.new(*sanitize_bounds(from, to), extracted[:exclude_end])
32
32
  end
33
33
 
34
34
  def serialize(value)
@@ -76,6 +76,15 @@ module ActiveRecord
76
76
  }
77
77
  end
78
78
 
79
+ INFINITE_FLOAT_RANGE = (-::Float::INFINITY)..(::Float::INFINITY) # :nodoc:
80
+
81
+ def sanitize_bounds(from, to)
82
+ [
83
+ (from == -::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(to)) ? nil : from,
84
+ (to == ::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(from)) ? nil : to
85
+ ]
86
+ end
87
+
79
88
  # When formatting the bound values of range types, PostgreSQL quotes
80
89
  # the bound value using double-quotes in certain conditions. Within
81
90
  # a double-quoted string, literal " and \ characters are themselves
@@ -13,9 +13,9 @@ module ActiveRecord
13
13
  return if value.blank?
14
14
 
15
15
  time = super
16
- return time if time.is_a?(ActiveSupport::TimeWithZone)
16
+ return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
17
17
 
18
- # While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to Postgres in UTC.
18
+ # While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to PostgreSQL in UTC.
19
19
  # We prefer times always in UTC, so here we convert back.
20
20
  if is_utc?
21
21
  time.getutc
@@ -4,19 +4,48 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module YugabyteDB
6
6
  module Quoting
7
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
9
+
10
+ class IntegerOutOf64BitRange < StandardError
11
+ def initialize(msg)
12
+ super(msg)
13
+ end
14
+ end
15
+
7
16
  # Escapes binary strings for bytea input to the database.
8
17
  def escape_bytea(value)
9
- @connection.escape_bytea(value) if value
18
+ valid_raw_connection.escape_bytea(value) if value
10
19
  end
11
20
 
12
21
  # Unescapes bytea output from a database to the binary string it represents.
13
22
  # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
14
23
  # on escaped binary output from database drive.
15
24
  def unescape_bytea(value)
16
- @connection.unescape_bytea(value) if value
25
+ valid_raw_connection.unescape_bytea(value) if value
26
+ end
27
+
28
+ def check_int_in_range(value)
29
+ if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
30
+ exception = <<~ERROR
31
+ Provided value outside of the range of a signed 64bit integer.
32
+
33
+ PostgreSQL will treat the column type in question as a numeric.
34
+ This may result in a slow sequential scan due to a comparison
35
+ being performed between an integer or bigint value and a numeric value.
36
+
37
+ To allow for this potentially unwanted behavior, set
38
+ ActiveRecord.raise_int_wider_than_64bit to false.
39
+ ERROR
40
+ raise IntegerOutOf64BitRange.new exception
41
+ end
17
42
  end
18
43
 
19
44
  def quote(value) # :nodoc:
45
+ if ActiveRecord.raise_int_wider_than_64bit && value.is_a?(Integer)
46
+ check_int_in_range(value)
47
+ end
48
+
20
49
  case value
21
50
  when OID::Xml::Data
22
51
  "xml '#{quote_string(value.to_s)}'"
@@ -43,7 +72,9 @@ module ActiveRecord
43
72
 
44
73
  # Quotes strings for use in SQL input.
45
74
  def quote_string(s) # :nodoc:
46
- @connection.escape(s)
75
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
76
+ connection.escape(s)
77
+ end
47
78
  end
48
79
 
49
80
  # Checks the following cases:
@@ -55,12 +86,12 @@ module ActiveRecord
55
86
  # - "schema.name".table_name
56
87
  # - "schema.name"."table.name"
57
88
  def quote_table_name(name) # :nodoc:
58
- self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
89
+ QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
59
90
  end
60
91
 
61
92
  # Quotes schema names for use in SQL queries.
62
93
  def quote_schema_name(name)
63
- YugabyteYSQL::Connection.quote_ident(name)
94
+ YSQL::Connection.quote_ident(name)
64
95
  end
65
96
 
66
97
  def quote_table_name_for_assignment(table, attr)
@@ -69,7 +100,7 @@ module ActiveRecord
69
100
 
70
101
  # Quotes column names for use in SQL queries.
71
102
  def quote_column_name(name) # :nodoc:
72
- self.class.quoted_column_names[name] ||= YugabyteYSQL::Connection.quote_ident(super).freeze
103
+ QUOTED_COLUMN_NAMES[name] ||= YSQL::Connection.quote_ident(super).freeze
73
104
  end
74
105
 
75
106
  # Quote date/time values for use in SQL input.
@@ -89,7 +120,7 @@ module ActiveRecord
89
120
  def quote_default_expression(value, column) # :nodoc:
90
121
  if value.is_a?(Proc)
91
122
  value.call
92
- elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
123
+ elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
93
124
  value # Does not quote function default values for UUID columns
94
125
  elsif column.respond_to?(:array?)
95
126
  type = lookup_cast_type_from_column(column)
@@ -118,6 +149,7 @@ module ActiveRecord
118
149
  end
119
150
 
120
151
  def lookup_cast_type_from_column(column) # :nodoc:
152
+ verify! if type_map.nil?
121
153
  type_map.lookup(column.oid, column.fmod, column.sql_type)
122
154
  end
123
155
 
@@ -134,7 +166,7 @@ module ActiveRecord
134
166
  (
135
167
  (?:
136
168
  # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
137
- ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
169
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
138
170
  )
139
171
  (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
140
172
  )
@@ -147,8 +179,9 @@ module ActiveRecord
147
179
  (
148
180
  (?:
149
181
  # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
150
- ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
182
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
151
183
  )
184
+ (?:\s+COLLATE\s+"\w+")?
152
185
  (?:\s+ASC|\s+DESC)?
153
186
  (?:\s+NULLS\s+(?:FIRST|LAST))?
154
187
  )
@@ -38,7 +38,7 @@ Rails needs superuser privileges to disable referential integrity.
38
38
  end
39
39
  end
40
40
 
41
- def all_foreign_keys_valid? # :nodoc:
41
+ def check_all_foreign_keys_valid! # :nodoc:
42
42
  sql = <<~SQL
43
43
  do $$
44
44
  declare r record;
@@ -61,14 +61,8 @@ Rails needs superuser privileges to disable referential integrity.
61
61
  $$;
62
62
  SQL
63
63
 
64
- begin
65
- transaction(requires_new: true) do
66
- execute(sql)
67
- end
68
-
69
- true
70
- rescue ActiveRecord::StatementInvalid
71
- false
64
+ transaction(requires_new: true) do
65
+ execute(sql)
72
66
  end
73
67
  end
74
68
  end