activerecord-cockroachdb-adapter 0.2.3 → 5.2.2
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 +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
|