activerecord-cockroachdb-adapter 0.2.3 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.gitmodules +0 -3
- data/CONTRIBUTING.md +25 -53
- data/Gemfile +58 -6
- data/README.md +293 -2
- data/Rakefile +17 -5
- data/activerecord-cockroachdb-adapter.gemspec +3 -6
- data/build/Dockerfile +1 -1
- data/build/teamcity-test.sh +17 -37
- data/docker.sh +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/arel_tosql.rb +27 -0
- data/lib/active_record/connection_adapters/cockroachdb/attribute_methods.rb +28 -0
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +94 -0
- data/lib/active_record/connection_adapters/cockroachdb/column_methods.rb +53 -0
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +102 -0
- data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +121 -0
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +37 -0
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +23 -38
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +123 -40
- data/lib/active_record/connection_adapters/cockroachdb/setup.rb +19 -0
- data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +44 -0
- data/lib/active_record/connection_adapters/cockroachdb/table_definition.rb +56 -0
- data/lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb +14 -16
- data/lib/active_record/connection_adapters/cockroachdb/type.rb +14 -0
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +218 -123
- metadata +18 -42
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module CockroachDB # :nodoc:
|
6
|
+
class TableDefinition < PostgreSQL::TableDefinition # :nodoc:
|
7
|
+
include ColumnMethods
|
8
|
+
|
9
|
+
# Support for spatial columns in tables
|
10
|
+
# super: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
|
11
|
+
def new_column_definition(name, type, **options)
|
12
|
+
if (info = CockroachDBAdapter.spatial_column_options(type.to_sym))
|
13
|
+
if (limit = options.delete(:limit)) && limit.is_a?(::Hash)
|
14
|
+
options.merge!(limit)
|
15
|
+
end
|
16
|
+
|
17
|
+
geo_type = ColumnDefinitionUtils.geo_type(options[:type] || type || info[:type])
|
18
|
+
base_type = info[:type] || (options[:geographic] ? :geography : :geometry)
|
19
|
+
|
20
|
+
options[:limit] = ColumnDefinitionUtils.limit_from_options(geo_type, options)
|
21
|
+
options[:spatial_type] = geo_type
|
22
|
+
column = super(name, base_type, **options)
|
23
|
+
else
|
24
|
+
column = super(name, type, **options)
|
25
|
+
end
|
26
|
+
|
27
|
+
column
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ColumnDefinitionUtils
|
32
|
+
class << self
|
33
|
+
def geo_type(type = 'GEOMETRY')
|
34
|
+
g_type = type.to_s.delete('_').upcase
|
35
|
+
return 'POINT' if g_type == 'STPOINT'
|
36
|
+
return 'POLYGON' if g_type == 'STPOLYGON'
|
37
|
+
|
38
|
+
g_type
|
39
|
+
end
|
40
|
+
|
41
|
+
def limit_from_options(type, options = {})
|
42
|
+
spatial_type = geo_type(type)
|
43
|
+
spatial_type << 'Z' if options[:has_z]
|
44
|
+
spatial_type << 'M' if options[:has_m]
|
45
|
+
spatial_type << ",#{options[:srid] || default_srid(options)}"
|
46
|
+
spatial_type
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_srid(options)
|
50
|
+
options[:geographic] ? 4326 : CockroachDBAdapter::DEFAULT_SRID
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,25 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_record/connection_adapters/abstract/transaction'
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module ConnectionAdapters
|
7
|
-
|
8
|
-
# NOTE(joey): This is a very sad monkey patch. Unfortunately, it is
|
9
|
-
# required in order to prevent doing more than 2 nested transactions
|
10
|
-
# while still allowing a single nested transaction. This is because
|
11
|
-
# CockroachDB only supports a single savepoint at the beginning of a
|
12
|
-
# transaction. Allowing this works for the common case of testing.
|
13
5
|
module CockroachDB
|
14
6
|
module TransactionManagerMonkeyPatch
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
7
|
+
# Capture ActiveRecord::SerializationFailure errors caused by
|
8
|
+
# transactions that fail due to serialization errors. Failed
|
9
|
+
# transactions will be retried until they pass or the max retry limit is
|
10
|
+
# exceeded.
|
11
|
+
def within_new_transaction(options = {})
|
12
|
+
attempts = options.fetch(:attempts, 0)
|
13
|
+
super
|
14
|
+
rescue ActiveRecord::SerializationFailure => error
|
15
|
+
raise if attempts >= @connection.max_transaction_retries
|
16
|
+
|
17
|
+
attempts += 1
|
18
|
+
sleep_seconds = (2 ** attempts + rand) / 10
|
19
|
+
sleep(sleep_seconds)
|
20
|
+
within_new_transaction(options.merge(attempts: attempts)) { yield }
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Type
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
# Return :postgresql instead of :cockroachdb for current_adapter_name so
|
7
|
+
# we can continue using the ActiveRecord::Types defined in
|
8
|
+
# PostgreSQLAdapter.
|
9
|
+
def current_adapter_name
|
10
|
+
:postgresql
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,8 +1,24 @@
|
|
1
|
+
require "rgeo/active_record"
|
2
|
+
|
1
3
|
require 'active_record/connection_adapters/postgresql_adapter'
|
2
|
-
require "active_record/connection_adapters/
|
4
|
+
require "active_record/connection_adapters/cockroachdb/column_methods"
|
3
5
|
require "active_record/connection_adapters/cockroachdb/schema_statements"
|
4
6
|
require "active_record/connection_adapters/cockroachdb/referential_integrity"
|
5
7
|
require "active_record/connection_adapters/cockroachdb/transaction_manager"
|
8
|
+
require "active_record/connection_adapters/cockroachdb/database_statements"
|
9
|
+
require "active_record/connection_adapters/cockroachdb/table_definition"
|
10
|
+
require "active_record/connection_adapters/cockroachdb/quoting"
|
11
|
+
require "active_record/connection_adapters/cockroachdb/type"
|
12
|
+
require "active_record/connection_adapters/cockroachdb/attribute_methods"
|
13
|
+
require "active_record/connection_adapters/cockroachdb/column"
|
14
|
+
require "active_record/connection_adapters/cockroachdb/spatial_column_info"
|
15
|
+
require "active_record/connection_adapters/cockroachdb/setup"
|
16
|
+
require "active_record/connection_adapters/cockroachdb/oid/spatial"
|
17
|
+
require "active_record/connection_adapters/cockroachdb/arel_tosql"
|
18
|
+
|
19
|
+
# Run to ignore spatial tables that will break schemna dumper.
|
20
|
+
# Defined in ./setup.rb
|
21
|
+
ActiveRecord::ConnectionAdapters::CockroachDB.initial_setup
|
6
22
|
|
7
23
|
module ActiveRecord
|
8
24
|
module ConnectionHandling
|
@@ -32,16 +48,115 @@ module ActiveRecord
|
|
32
48
|
module ConnectionAdapters
|
33
49
|
class CockroachDBAdapter < PostgreSQLAdapter
|
34
50
|
ADAPTER_NAME = "CockroachDB".freeze
|
51
|
+
DEFAULT_PRIMARY_KEY = "rowid"
|
52
|
+
|
53
|
+
SPATIAL_COLUMN_OPTIONS =
|
54
|
+
{
|
55
|
+
geography: { geographic: true },
|
56
|
+
geometry: {},
|
57
|
+
geometry_collection: {},
|
58
|
+
line_string: {},
|
59
|
+
multi_line_string: {},
|
60
|
+
multi_point: {},
|
61
|
+
multi_polygon: {},
|
62
|
+
spatial: {},
|
63
|
+
st_point: {},
|
64
|
+
st_polygon: {},
|
65
|
+
}
|
66
|
+
|
67
|
+
# http://postgis.17.x6.nabble.com/Default-SRID-td5001115.html
|
68
|
+
DEFAULT_SRID = 0
|
35
69
|
|
36
70
|
include CockroachDB::SchemaStatements
|
37
71
|
include CockroachDB::ReferentialIntegrity
|
72
|
+
include CockroachDB::DatabaseStatements
|
73
|
+
include CockroachDB::Quoting
|
74
|
+
|
75
|
+
# override
|
76
|
+
# This method makes a sql query to gather information about columns
|
77
|
+
# in a table. It returns an array of arrays (one for each col) and
|
78
|
+
# is mapped to columns in the SchemaStatements#columns method.
|
79
|
+
#
|
80
|
+
# The issue with the default method is that the sql_type field is
|
81
|
+
# retrieved with the `format_type` function, but this is implemented
|
82
|
+
# differently in CockroachDB than PostGIS, so geometry/geography
|
83
|
+
# types are missing information which makes parsing them impossible.
|
84
|
+
# Below is an example of what `format_type` returns for a geometry
|
85
|
+
# column.
|
86
|
+
#
|
87
|
+
# column_type: geometry(POINT, 4326)
|
88
|
+
# Expected: geometry(POINT, 4326)
|
89
|
+
# Actual: geometry
|
90
|
+
#
|
91
|
+
# The solution is to make the default query with super, then
|
92
|
+
# iterate through the columns and if it is a spatial type,
|
93
|
+
# access the proper column_type with the information_schema.columns
|
94
|
+
# table.
|
95
|
+
#
|
96
|
+
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
97
|
+
def column_definitions(table_name)
|
98
|
+
fields = super
|
99
|
+
# iterate through and identify all spatial fields based on format_type
|
100
|
+
# being geometry or geography, then query for the information_schema.column
|
101
|
+
# column_type because that contains the necessary information.
|
102
|
+
fields.map do |field|
|
103
|
+
dtype = field[1]
|
104
|
+
if dtype == 'geometry' || dtype == 'geography'
|
105
|
+
col_name = field[0]
|
106
|
+
data_type = \
|
107
|
+
query(<<~SQL, "SCHEMA")
|
108
|
+
SELECT c.data_type
|
109
|
+
FROM information_schema.columns c
|
110
|
+
WHERE c.table_name = #{quote(table_name)}
|
111
|
+
AND c.column_name = #{quote(col_name)}
|
112
|
+
SQL
|
113
|
+
field[1] = data_type[0][0]
|
114
|
+
end
|
115
|
+
field
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def arel_visitor
|
120
|
+
Arel::Visitors::CockroachDB.new(self)
|
121
|
+
end
|
38
122
|
|
123
|
+
def self.spatial_column_options(key)
|
124
|
+
SPATIAL_COLUMN_OPTIONS[key]
|
125
|
+
end
|
39
126
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
127
|
+
def postgis_lib_version
|
128
|
+
@postgis_lib_version ||= select_value("SELECT PostGIS_Lib_Version()")
|
129
|
+
end
|
130
|
+
|
131
|
+
def default_srid
|
132
|
+
DEFAULT_SRID
|
133
|
+
end
|
134
|
+
|
135
|
+
def srs_database_columns
|
136
|
+
{
|
137
|
+
auth_name_column: "auth_name",
|
138
|
+
auth_srid_column: "auth_srid",
|
139
|
+
proj4text_column: "proj4text",
|
140
|
+
srtext_column: "srtext",
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def debugging?
|
145
|
+
!!ENV["DEBUG_COCKROACHDB_ADAPTER"]
|
146
|
+
end
|
147
|
+
|
148
|
+
def max_transaction_retries
|
149
|
+
@max_transaction_retries ||= @config.fetch(:max_transaction_retries, 3)
|
150
|
+
end
|
151
|
+
|
152
|
+
# CockroachDB 20.1 can run queries that work against PostgreSQL 10+.
|
153
|
+
def postgresql_version
|
154
|
+
100000
|
155
|
+
end
|
156
|
+
|
157
|
+
def supports_bulk_alter?
|
158
|
+
false
|
159
|
+
end
|
45
160
|
|
46
161
|
def supports_json?
|
47
162
|
# FIXME(joey): Add a version check.
|
@@ -65,13 +180,8 @@ module ActiveRecord
|
|
65
180
|
false
|
66
181
|
end
|
67
182
|
|
68
|
-
def supports_pg_crypto_uuid?
|
69
|
-
false
|
70
|
-
end
|
71
|
-
|
72
183
|
def supports_partial_index?
|
73
|
-
|
74
|
-
false
|
184
|
+
@crdb_version >= 202
|
75
185
|
end
|
76
186
|
|
77
187
|
def supports_expression_index?
|
@@ -94,8 +204,7 @@ module ActiveRecord
|
|
94
204
|
end
|
95
205
|
|
96
206
|
def supports_advisory_locks?
|
97
|
-
|
98
|
-
true
|
207
|
+
false
|
99
208
|
end
|
100
209
|
|
101
210
|
def supports_virtual_columns?
|
@@ -103,114 +212,8 @@ module ActiveRecord
|
|
103
212
|
false
|
104
213
|
end
|
105
214
|
|
106
|
-
def
|
107
|
-
|
108
|
-
false
|
109
|
-
end
|
110
|
-
|
111
|
-
def transaction_isolation_levels
|
112
|
-
{
|
113
|
-
# Explicitly prevent READ UNCOMMITTED from being used. This
|
114
|
-
# was due to the READ UNCOMMITTED test failing.
|
115
|
-
# read_uncommitted: "READ UNCOMMITTED",
|
116
|
-
read_committed: "READ COMMITTED",
|
117
|
-
repeatable_read: "REPEATABLE READ",
|
118
|
-
serializable: "SERIALIZABLE"
|
119
|
-
}
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
# Sadly, we can only do savepoints at the beginning of
|
124
|
-
# transactions. This means that we cannot use them for most cases
|
125
|
-
# of transaction, so we just pretend they're usable.
|
126
|
-
def create_savepoint(name = "COCKROACH_RESTART"); end
|
127
|
-
|
128
|
-
def exec_rollback_to_savepoint(name = "COCKROACH_RESTART"); end
|
129
|
-
|
130
|
-
def release_savepoint(name = "COCKROACH_RESTART"); end
|
131
|
-
|
132
|
-
def indexes(table_name, name = nil) # :nodoc:
|
133
|
-
# The PostgreSQL adapter uses a correlated subquery in the following query,
|
134
|
-
# which CockroachDB does not yet support. That portion of the query fetches
|
135
|
-
# any non-standard opclasses that each index uses. CockroachDB also doesn't
|
136
|
-
# support opclasses at this time, so the query is modified to just remove
|
137
|
-
# the section about opclasses entirely.
|
138
|
-
if name
|
139
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
140
|
-
Passing name to #indexes is deprecated without replacement.
|
141
|
-
MSG
|
142
|
-
end
|
143
|
-
|
144
|
-
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
145
|
-
|
146
|
-
result = query(<<-SQL, "SCHEMA")
|
147
|
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
148
|
-
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
149
|
-
FROM pg_class t
|
150
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
151
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
152
|
-
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
153
|
-
WHERE i.relkind = 'i'
|
154
|
-
AND d.indisprimary = 'f'
|
155
|
-
AND t.relname = '#{table.identifier}'
|
156
|
-
AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
|
157
|
-
ORDER BY i.relname
|
158
|
-
SQL
|
159
|
-
|
160
|
-
result.map do |row|
|
161
|
-
index_name = row[0]
|
162
|
-
unique = row[1]
|
163
|
-
indkey = row[2].split(" ").map(&:to_i)
|
164
|
-
inddef = row[3]
|
165
|
-
oid = row[4]
|
166
|
-
comment = row[5]
|
167
|
-
|
168
|
-
expressions, where = inddef.scan(/\((.+?)\)(?: WHERE (.+))?\z/).flatten
|
169
|
-
|
170
|
-
if indkey.include?(0)
|
171
|
-
columns = expressions
|
172
|
-
else
|
173
|
-
columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
|
174
|
-
SELECT a.attnum, a.attname
|
175
|
-
FROM pg_attribute a
|
176
|
-
WHERE a.attrelid = #{oid}
|
177
|
-
AND a.attnum IN (#{indkey.join(",")})
|
178
|
-
SQL
|
179
|
-
|
180
|
-
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
181
|
-
orders = Hash[
|
182
|
-
expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
|
183
|
-
]
|
184
|
-
end
|
185
|
-
|
186
|
-
# FIXME(joey): This may be specific to ActiveRecord 5.2.
|
187
|
-
IndexDefinition.new(
|
188
|
-
table_name,
|
189
|
-
index_name,
|
190
|
-
unique,
|
191
|
-
columns,
|
192
|
-
orders: orders,
|
193
|
-
where: where,
|
194
|
-
comment: comment.presence
|
195
|
-
)
|
196
|
-
end.compact
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
def primary_keys(table_name)
|
201
|
-
name = Utils.extract_schema_qualified_name(table_name.to_s)
|
202
|
-
select_values(<<-SQL.strip_heredoc, "SCHEMA")
|
203
|
-
SELECT column_name
|
204
|
-
FROM information_schema.key_column_usage kcu
|
205
|
-
JOIN information_schema.table_constraints tc
|
206
|
-
ON kcu.table_name = tc.table_name
|
207
|
-
AND kcu.table_schema = tc.table_schema
|
208
|
-
AND kcu.constraint_name = tc.constraint_name
|
209
|
-
WHERE constraint_type = 'PRIMARY KEY'
|
210
|
-
AND kcu.table_name = #{quote(name.identifier)}
|
211
|
-
AND kcu.table_schema = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
|
212
|
-
ORDER BY kcu.ordinal_position
|
213
|
-
SQL
|
215
|
+
def supports_string_to_array_coercion?
|
216
|
+
@crdb_version >= 202
|
214
217
|
end
|
215
218
|
|
216
219
|
# This is hardcoded to 63 (as previously was in ActiveRecord 5.0) to aid in
|
@@ -228,10 +231,44 @@ module ActiveRecord
|
|
228
231
|
alias index_name_length max_identifier_length
|
229
232
|
alias table_alias_length max_identifier_length
|
230
233
|
|
234
|
+
def initialize(connection, logger, conn_params, config)
|
235
|
+
super(connection, logger, conn_params, config)
|
236
|
+
crdb_version_string = query_value("SHOW crdb_version")
|
237
|
+
if crdb_version_string.include? "v1."
|
238
|
+
version_num = 1
|
239
|
+
elsif crdb_version_string.include? "v2."
|
240
|
+
version_num 2
|
241
|
+
elsif crdb_version_string.include? "v19.1."
|
242
|
+
version_num = 191
|
243
|
+
elsif crdb_version_string.include? "v19.2."
|
244
|
+
version_num = 192
|
245
|
+
elsif crdb_version_string.include? "v20.1."
|
246
|
+
version_num = 201
|
247
|
+
else
|
248
|
+
version_num = 202
|
249
|
+
end
|
250
|
+
@crdb_version = version_num
|
251
|
+
end
|
252
|
+
|
231
253
|
private
|
232
254
|
|
233
255
|
def initialize_type_map(m = type_map)
|
234
|
-
|
256
|
+
%w(
|
257
|
+
geography
|
258
|
+
geometry
|
259
|
+
geometry_collection
|
260
|
+
line_string
|
261
|
+
multi_line_string
|
262
|
+
multi_point
|
263
|
+
multi_polygon
|
264
|
+
st_point
|
265
|
+
st_polygon
|
266
|
+
).each do |geo_type|
|
267
|
+
m.register_type(geo_type) do |oid, _, sql_type|
|
268
|
+
CockroachDB::OID::Spatial.new(oid, sql_type)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
235
272
|
# NOTE(joey): PostgreSQL intervals have a precision.
|
236
273
|
# CockroachDB intervals do not, so overide the type
|
237
274
|
# definition. Returning a ArgumentError may not be correct.
|
@@ -243,6 +280,8 @@ module ActiveRecord
|
|
243
280
|
end
|
244
281
|
OID::SpecializedString.new(:interval, precision: precision)
|
245
282
|
end
|
283
|
+
|
284
|
+
super(m)
|
246
285
|
end
|
247
286
|
|
248
287
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -295,6 +334,62 @@ module ActiveRecord
|
|
295
334
|
end
|
296
335
|
end
|
297
336
|
|
337
|
+
# Override extract_value_from_default because the upstream definition
|
338
|
+
# doesn't handle the variations in CockroachDB's behavior.
|
339
|
+
def extract_value_from_default(default)
|
340
|
+
super ||
|
341
|
+
extract_escaped_string_from_default(default) ||
|
342
|
+
extract_time_from_default(default) ||
|
343
|
+
extract_empty_array_from_default(default)
|
344
|
+
end
|
345
|
+
|
346
|
+
# Both PostgreSQL and CockroachDB use C-style string escapes under the
|
347
|
+
# covers. PostgreSQL obscures this for us and unescapes the strings, but
|
348
|
+
# CockroachDB does not. Here we'll use Ruby to unescape the string.
|
349
|
+
# See https://github.com/cockroachdb/cockroach/issues/47497 and
|
350
|
+
# https://www.postgresql.org/docs/9.2/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE.
|
351
|
+
def extract_escaped_string_from_default(default)
|
352
|
+
# Escaped strings start with an e followed by the string in quotes (e'…')
|
353
|
+
return unless default =~ /\A[\(B]?e'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
354
|
+
|
355
|
+
# String#undump doesn't account for escaped single quote characters
|
356
|
+
"\"#{$1}\"".undump.gsub("\\'".freeze, "'".freeze)
|
357
|
+
end
|
358
|
+
|
359
|
+
# This method exists to extract the correct time and date defaults for a
|
360
|
+
# couple of reasons.
|
361
|
+
# 1) There's a bug in CockroachDB where the date type is missing from
|
362
|
+
# the column info query.
|
363
|
+
# https://github.com/cockroachdb/cockroach/issues/47285
|
364
|
+
# 2) PostgreSQL's timestamp without time zone type maps to CockroachDB's
|
365
|
+
# TIMESTAMP type. TIMESTAMP includes a UTC time zone while timestamp
|
366
|
+
# without time zone doesn't.
|
367
|
+
# https://www.cockroachlabs.com/docs/v19.2/timestamp.html#variants
|
368
|
+
def extract_time_from_default(default)
|
369
|
+
return unless default =~ /\A'(.*)'\z/
|
370
|
+
|
371
|
+
# If default has a UTC time zone, we'll drop the time zone information
|
372
|
+
# so it acts like PostgreSQL's timestamp without time zone. Then, try
|
373
|
+
# to parse the resulting string to verify if it's a time.
|
374
|
+
time = if default =~ /\A'(.*)(\+00:00)'\z/
|
375
|
+
$1
|
376
|
+
else
|
377
|
+
default
|
378
|
+
end
|
379
|
+
|
380
|
+
Time.parse(time).to_s
|
381
|
+
rescue
|
382
|
+
nil
|
383
|
+
end
|
384
|
+
|
385
|
+
# CockroachDB stores default values for arrays in the `ARRAY[...]` format.
|
386
|
+
# In general, it is hard to parse that, but it is easy to handle the common
|
387
|
+
# case of an empty array.
|
388
|
+
def extract_empty_array_from_default(default)
|
389
|
+
return unless supports_string_to_array_coercion?
|
390
|
+
return unless default =~ /\AARRAY\[\]\z/
|
391
|
+
return "{}"
|
392
|
+
end
|
298
393
|
|
299
394
|
# end private
|
300
395
|
end
|