activerecord-jdbc-adapter 52.8-java → 60.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/.travis.yml +58 -37
- data/Gemfile +9 -2
- data/README.md +25 -9
- data/Rakefile +1 -1
- data/Rakefile.jdbc +8 -1
- data/activerecord-jdbc-adapter.gemspec +5 -8
- data/lib/arjdbc/abstract/connection_management.rb +7 -0
- data/lib/arjdbc/abstract/core.rb +16 -23
- data/lib/arjdbc/abstract/database_statements.rb +26 -2
- data/lib/arjdbc/abstract/statement_cache.rb +2 -5
- data/lib/arjdbc/abstract/transaction_support.rb +5 -3
- data/lib/arjdbc/db2/column.rb +0 -39
- data/lib/arjdbc/derby/adapter.rb +1 -20
- data/lib/arjdbc/firebird/adapter.rb +0 -21
- data/lib/arjdbc/h2/adapter.rb +0 -15
- data/lib/arjdbc/hsqldb/adapter.rb +0 -14
- data/lib/arjdbc/informix/adapter.rb +0 -23
- data/lib/arjdbc/jdbc/adapter.rb +3 -1
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
- data/lib/arjdbc/jdbc/base_ext.rb +3 -1
- data/lib/arjdbc/jdbc/callbacks.rb +2 -0
- data/lib/arjdbc/jdbc/column.rb +2 -0
- data/lib/arjdbc/jdbc/connection.rb +2 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
- data/lib/arjdbc/jdbc/error.rb +2 -0
- data/lib/arjdbc/jdbc/extension.rb +2 -0
- data/lib/arjdbc/jdbc/java.rb +3 -1
- data/lib/arjdbc/jdbc/railtie.rb +3 -1
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
- data/lib/arjdbc/jdbc/type_cast.rb +2 -0
- data/lib/arjdbc/jdbc/type_converter.rb +2 -0
- data/lib/arjdbc/mysql/adapter.rb +47 -18
- data/lib/arjdbc/mysql/connection_methods.rb +0 -1
- data/lib/arjdbc/postgresql/adapter.rb +220 -213
- data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
- data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
- data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
- data/lib/arjdbc/postgresql/column.rb +6 -4
- data/lib/arjdbc/postgresql/connection_methods.rb +0 -1
- data/lib/arjdbc/postgresql/name.rb +2 -0
- data/lib/arjdbc/postgresql/oid_types.rb +2 -0
- data/lib/arjdbc/sqlite3/adapter.rb +175 -180
- data/lib/arjdbc/sqlite3/connection_methods.rb +15 -5
- data/lib/arjdbc/tasks/databases.rake +13 -10
- data/lib/arjdbc/util/quoted_cache.rb +3 -1
- data/lib/arjdbc/util/serialized_attributes.rb +3 -1
- data/lib/arjdbc/util/table_copier.rb +3 -1
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +4 -4
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/rails.rake +1 -1
- data/src/java/arjdbc/ArJdbcModule.java +5 -5
- data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +434 -701
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +0 -51
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +31 -24
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
- metadata +7 -9
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
ArJdbc.load_java_part :PostgreSQL
|
3
3
|
|
4
4
|
require 'ipaddr'
|
@@ -42,46 +42,52 @@ module ArJdbc
|
|
42
42
|
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
|
43
43
|
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end
|
44
44
|
|
45
|
-
ADAPTER_NAME = 'PostgreSQL'
|
45
|
+
ADAPTER_NAME = 'PostgreSQL'
|
46
46
|
|
47
47
|
def adapter_name
|
48
48
|
ADAPTER_NAME
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
version[0] * 100 * 100 + version[1]
|
71
|
-
else
|
72
|
-
(version[0] * 100 + version[1]) * 100
|
73
|
-
end
|
74
|
-
elsif version.size == 1
|
75
|
-
version[0] * 100 * 100
|
51
|
+
def get_database_version # :nodoc:
|
52
|
+
begin
|
53
|
+
version = @connection.database_product
|
54
|
+
if match = version.match(/([\d\.]*\d).*?/)
|
55
|
+
version = match[1].split('.').map(&:to_i)
|
56
|
+
# PostgreSQL version representation does not have more than 4 digits
|
57
|
+
# From version 10 onwards, PG has changed its versioning policy to
|
58
|
+
# limit it to only 2 digits. i.e. in 10.x, 10 being the major
|
59
|
+
# version and x representing the patch release
|
60
|
+
# Refer to:
|
61
|
+
# https://www.postgresql.org/support/versioning/
|
62
|
+
# https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
|
63
|
+
# for more info
|
64
|
+
|
65
|
+
if version.size >= 3
|
66
|
+
(version[0] * 100 + version[1]) * 100 + version[2]
|
67
|
+
elsif version.size == 2
|
68
|
+
if version[0] >= 10
|
69
|
+
version[0] * 100 * 100 + version[1]
|
76
70
|
else
|
77
|
-
0
|
71
|
+
(version[0] * 100 + version[1]) * 100
|
78
72
|
end
|
73
|
+
elsif version.size == 1
|
74
|
+
version[0] * 100 * 100
|
79
75
|
else
|
80
76
|
0
|
81
77
|
end
|
78
|
+
else
|
79
|
+
0
|
82
80
|
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_version # :nodoc:
|
85
|
+
if database_version < 90300
|
86
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
87
|
+
end
|
83
88
|
end
|
84
89
|
|
90
|
+
|
85
91
|
def redshift?
|
86
92
|
# SELECT version() :
|
87
93
|
# PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.647
|
@@ -92,13 +98,6 @@ module ArJdbc
|
|
92
98
|
end
|
93
99
|
private :redshift?
|
94
100
|
|
95
|
-
def use_insert_returning?
|
96
|
-
if @use_insert_returning.nil?
|
97
|
-
@use_insert_returning = supports_insert_with_returning?
|
98
|
-
end
|
99
|
-
@use_insert_returning
|
100
|
-
end
|
101
|
-
|
102
101
|
def set_client_encoding(encoding)
|
103
102
|
ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
|
104
103
|
ActiveRecord::Base.logger.debug "Set the 'allowEncodingChanges' driver property (e.g. using config[:properties]) if you need to override the client encoding when doing a copy."
|
@@ -165,7 +164,7 @@ module ArJdbc
|
|
165
164
|
int4range: { name: 'int4range' },
|
166
165
|
int8range: { name: 'int8range' },
|
167
166
|
integer: { name: 'integer' },
|
168
|
-
interval: { name: 'interval' },
|
167
|
+
interval: { name: 'interval' },
|
169
168
|
json: { name: 'json' },
|
170
169
|
jsonb: { name: 'jsonb' },
|
171
170
|
line: { name: 'line' },
|
@@ -198,107 +197,114 @@ module ArJdbc
|
|
198
197
|
!native_database_types[type].nil?
|
199
198
|
end
|
200
199
|
|
201
|
-
# Enable standard-conforming strings if available.
|
202
200
|
def set_standard_conforming_strings
|
203
|
-
|
201
|
+
execute("SET standard_conforming_strings = on", "SCHEMA")
|
204
202
|
end
|
205
203
|
|
206
|
-
|
207
|
-
|
208
|
-
client_min_messages = self.client_min_messages
|
209
|
-
begin
|
210
|
-
self.client_min_messages = 'panic'
|
211
|
-
value = enable ? "on" : "off"
|
212
|
-
execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
|
213
|
-
@standard_conforming_strings = ( value == "on" )
|
214
|
-
rescue
|
215
|
-
@standard_conforming_strings = :unsupported
|
216
|
-
ensure
|
217
|
-
self.client_min_messages = client_min_messages
|
218
|
-
end
|
204
|
+
def supports_bulk_alter?
|
205
|
+
true
|
219
206
|
end
|
220
207
|
|
221
|
-
def
|
222
|
-
|
223
|
-
client_min_messages = self.client_min_messages
|
224
|
-
begin
|
225
|
-
self.client_min_messages = 'panic'
|
226
|
-
value = select_one('SHOW standard_conforming_strings', 'SCHEMA')['standard_conforming_strings']
|
227
|
-
@standard_conforming_strings = ( value == "on" )
|
228
|
-
rescue
|
229
|
-
@standard_conforming_strings = :unsupported
|
230
|
-
ensure
|
231
|
-
self.client_min_messages = client_min_messages
|
232
|
-
end
|
233
|
-
end
|
234
|
-
@standard_conforming_strings == true # return false if :unsupported
|
208
|
+
def supports_index_sort_order?
|
209
|
+
true
|
235
210
|
end
|
236
211
|
|
237
|
-
def
|
212
|
+
def supports_partial_index?
|
213
|
+
true
|
214
|
+
end
|
238
215
|
|
239
|
-
def
|
216
|
+
def supports_expression_index?
|
217
|
+
true
|
218
|
+
end
|
240
219
|
|
241
|
-
def
|
220
|
+
def supports_transaction_isolation?
|
221
|
+
true
|
222
|
+
end
|
242
223
|
|
243
|
-
def
|
224
|
+
def supports_foreign_keys?
|
225
|
+
true
|
226
|
+
end
|
244
227
|
|
245
|
-
def
|
228
|
+
def supports_validate_constraints?
|
229
|
+
true
|
230
|
+
end
|
246
231
|
|
247
|
-
def
|
232
|
+
def supports_views?
|
233
|
+
true
|
234
|
+
end
|
248
235
|
|
249
|
-
def
|
236
|
+
def supports_datetime_with_precision?
|
237
|
+
true
|
238
|
+
end
|
250
239
|
|
251
|
-
def
|
240
|
+
def supports_json?
|
241
|
+
database_version >= 90200
|
242
|
+
end
|
252
243
|
|
253
|
-
def
|
244
|
+
def supports_comments?
|
245
|
+
true
|
246
|
+
end
|
254
247
|
|
255
|
-
def
|
248
|
+
def supports_savepoints?
|
249
|
+
true
|
250
|
+
end
|
256
251
|
|
257
|
-
def
|
252
|
+
def supports_insert_returning?
|
253
|
+
true
|
254
|
+
end
|
258
255
|
|
259
|
-
def
|
256
|
+
def supports_insert_on_conflict?
|
257
|
+
database_version >= 90500
|
258
|
+
end
|
259
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
260
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
261
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
260
262
|
|
261
|
-
def
|
263
|
+
def index_algorithms
|
264
|
+
{ concurrently: 'CONCURRENTLY' }
|
265
|
+
end
|
262
266
|
|
263
|
-
def
|
267
|
+
def supports_ddl_transactions?
|
268
|
+
true
|
269
|
+
end
|
264
270
|
|
265
|
-
|
266
|
-
|
267
|
-
standard_conforming_strings?
|
268
|
-
@standard_conforming_strings != :unsupported
|
271
|
+
def supports_advisory_locks?
|
272
|
+
true
|
269
273
|
end
|
270
274
|
|
271
|
-
def
|
272
|
-
|
275
|
+
def supports_explain?
|
276
|
+
true
|
273
277
|
end
|
274
278
|
|
275
|
-
def
|
276
|
-
|
279
|
+
def supports_extensions?
|
280
|
+
database_version >= 90200
|
277
281
|
end
|
278
282
|
|
279
|
-
def
|
280
|
-
|
283
|
+
def supports_ranges?
|
284
|
+
database_version >= 90200
|
281
285
|
end
|
282
286
|
|
283
|
-
def
|
284
|
-
|
287
|
+
def supports_materialized_views?
|
288
|
+
database_version >= 90300
|
285
289
|
end
|
286
290
|
|
287
|
-
def
|
288
|
-
|
291
|
+
def supports_foreign_tables? # we don't really support this yet, its a reminder :)
|
292
|
+
database_version >= 90300
|
289
293
|
end
|
290
294
|
|
291
295
|
def supports_pgcrypto_uuid?
|
292
|
-
|
296
|
+
database_version >= 90400
|
293
297
|
end
|
294
298
|
|
295
|
-
|
296
|
-
|
297
|
-
|
299
|
+
def supports_optimizer_hints?
|
300
|
+
unless defined?(@has_pg_hint_plan)
|
301
|
+
@has_pg_hint_plan = extension_available?("pg_hint_plan")
|
302
|
+
end
|
303
|
+
@has_pg_hint_plan
|
298
304
|
end
|
299
305
|
|
300
|
-
def
|
301
|
-
|
306
|
+
def supports_lazy_transactions?
|
307
|
+
true
|
302
308
|
end
|
303
309
|
|
304
310
|
# From AR 5.1 postgres_adapter.rb
|
@@ -306,65 +312,60 @@ module ArJdbc
|
|
306
312
|
index.using == :btree || super
|
307
313
|
end
|
308
314
|
|
315
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
316
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
317
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
318
|
+
end
|
319
|
+
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
|
320
|
+
end
|
321
|
+
|
322
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
323
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
324
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
325
|
+
end
|
326
|
+
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
327
|
+
end
|
328
|
+
|
309
329
|
def enable_extension(name)
|
310
|
-
|
330
|
+
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
|
331
|
+
reload_type_map
|
332
|
+
}
|
311
333
|
end
|
312
334
|
|
313
335
|
def disable_extension(name)
|
314
|
-
|
336
|
+
exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
|
337
|
+
reload_type_map
|
338
|
+
}
|
339
|
+
end
|
340
|
+
|
341
|
+
def extension_available?(name)
|
342
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
315
343
|
end
|
316
344
|
|
317
345
|
def extension_enabled?(name)
|
318
|
-
|
319
|
-
rows = select_rows("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)", 'SCHEMA')
|
320
|
-
available = rows.first.first # true/false or 't'/'f'
|
321
|
-
available == true || available == 't'
|
322
|
-
end
|
346
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
323
347
|
end
|
324
348
|
|
325
349
|
def extensions
|
326
|
-
|
327
|
-
rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
|
328
|
-
rows.map { |row| row.first }
|
329
|
-
else
|
330
|
-
[]
|
331
|
-
end
|
350
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
332
351
|
end
|
333
352
|
|
334
|
-
|
335
|
-
|
353
|
+
# Returns the configured supported identifier length supported by PostgreSQL
|
354
|
+
def max_identifier_length
|
355
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
336
356
|
end
|
337
357
|
|
338
|
-
# Set the authorized user for this session
|
358
|
+
# Set the authorized user for this session
|
339
359
|
def session_auth=(user)
|
340
360
|
clear_cache!
|
341
|
-
execute
|
361
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
342
362
|
end
|
343
363
|
|
344
|
-
|
345
|
-
|
346
|
-
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
347
|
-
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
|
348
|
-
end
|
349
|
-
select_value("SELECT pg_try_advisory_lock(#{lock_id});")
|
350
|
-
end
|
351
|
-
|
352
|
-
# Came from postgres_adapter
|
353
|
-
def release_advisory_lock(lock_id) # :nodoc:
|
354
|
-
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
355
|
-
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
|
356
|
-
end
|
357
|
-
select_value("SELECT pg_advisory_unlock(#{lock_id})")
|
358
|
-
end
|
359
|
-
|
360
|
-
# Returns the max identifier length supported by PostgreSQL
|
361
|
-
def max_identifier_length
|
362
|
-
@max_identifier_length ||= select_one('SHOW max_identifier_length', 'SCHEMA'.freeze)['max_identifier_length'].to_i
|
364
|
+
def use_insert_returning?
|
365
|
+
@use_insert_returning
|
363
366
|
end
|
364
|
-
alias table_alias_length max_identifier_length
|
365
|
-
alias index_name_length max_identifier_length
|
366
367
|
|
367
|
-
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
368
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
368
369
|
val = super
|
369
370
|
if !use_insert_returning? && pk
|
370
371
|
unless sequence_name
|
@@ -383,20 +384,12 @@ module ArJdbc
|
|
383
384
|
ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
|
384
385
|
end
|
385
386
|
|
386
|
-
#
|
387
|
-
#
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
# We need to use #deep_dup here because it appears that
|
393
|
-
# the java method is reusing an object in some cases
|
394
|
-
# which makes all of the entries in the "result"
|
395
|
-
# array end up with the same values as the last row
|
396
|
-
result << values.deep_dup
|
397
|
-
end
|
398
|
-
result
|
399
|
-
end
|
387
|
+
# from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
|
388
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
|
389
|
+
private_constant :READ_QUERY
|
390
|
+
|
391
|
+
def write_query?(sql) # :nodoc:
|
392
|
+
!READ_QUERY.match?(sql)
|
400
393
|
end
|
401
394
|
|
402
395
|
# We need to make sure to deallocate all the prepared statements
|
@@ -427,6 +420,24 @@ module ArJdbc
|
|
427
420
|
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
|
428
421
|
end
|
429
422
|
|
423
|
+
def build_insert_sql(insert) # :nodoc:
|
424
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
425
|
+
|
426
|
+
if insert.skip_duplicates?
|
427
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
428
|
+
elsif insert.update_duplicates?
|
429
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
430
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
431
|
+
end
|
432
|
+
|
433
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
434
|
+
sql
|
435
|
+
end
|
436
|
+
|
437
|
+
def build_truncate_statements(*table_names)
|
438
|
+
"TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
|
439
|
+
end
|
440
|
+
|
430
441
|
def all_schemas
|
431
442
|
select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
|
432
443
|
end
|
@@ -463,13 +474,7 @@ module ArJdbc
|
|
463
474
|
|
464
475
|
def escape_bytea(string)
|
465
476
|
return unless string
|
466
|
-
|
467
|
-
"\\x#{string.unpack("H*")[0]}"
|
468
|
-
else
|
469
|
-
result = ''
|
470
|
-
string.each_byte { |c| result << sprintf('\\\\%03o', c) }
|
471
|
-
result
|
472
|
-
end
|
477
|
+
"\\x#{string.unpack("H*")[0]}"
|
473
478
|
end
|
474
479
|
|
475
480
|
# @override
|
@@ -502,6 +507,19 @@ module ArJdbc
|
|
502
507
|
nil
|
503
508
|
end
|
504
509
|
|
510
|
+
|
511
|
+
# @private
|
512
|
+
def column_name_for_operation(operation, node)
|
513
|
+
case operation
|
514
|
+
when 'maximum' then 'max'
|
515
|
+
when 'minimum' then 'min'
|
516
|
+
when 'average' then 'avg'
|
517
|
+
else operation.downcase
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
private
|
522
|
+
|
505
523
|
# Returns the list of a table's column names, data types, and default values.
|
506
524
|
#
|
507
525
|
# If the table name is not prefixed with a schema, the database will
|
@@ -511,82 +529,73 @@ module ArJdbc
|
|
511
529
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
512
530
|
# - ::regclass is a function that gives the id for a table name
|
513
531
|
def column_definitions(table_name)
|
514
|
-
select_rows(
|
532
|
+
select_rows(<<~SQL, 'SCHEMA')
|
515
533
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
516
534
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
517
|
-
|
518
|
-
WHERE c.oid = a.attcollation AND t.oid = a.atttypid
|
519
|
-
AND a.attcollation <> t.typcollation),
|
520
|
-
col_description(a.attrelid, a.attnum) AS comment
|
535
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
521
536
|
FROM pg_attribute a
|
522
537
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
538
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
539
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
523
540
|
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
524
541
|
AND a.attnum > 0 AND NOT a.attisdropped
|
525
542
|
ORDER BY a.attnum
|
526
|
-
|
543
|
+
SQL
|
527
544
|
end
|
528
|
-
private :column_definitions
|
529
545
|
|
530
|
-
def
|
531
|
-
|
546
|
+
def extract_table_ref_from_insert_sql(sql)
|
547
|
+
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
548
|
+
$1.strip if $1
|
532
549
|
end
|
533
550
|
|
534
|
-
|
535
|
-
|
536
|
-
case operation
|
537
|
-
when 'maximum' then 'max'
|
538
|
-
when 'minimum' then 'min'
|
539
|
-
when 'average' then 'avg'
|
540
|
-
else operation.downcase
|
541
|
-
end
|
551
|
+
def arel_visitor
|
552
|
+
Arel::Visitors::PostgreSQL.new(self)
|
542
553
|
end
|
543
554
|
|
544
|
-
private
|
545
|
-
|
546
555
|
# Pulled from ActiveRecord's Postgres adapter and modified to use execute
|
547
556
|
def can_perform_case_insensitive_comparison_for?(column)
|
548
557
|
@case_insensitive_cache ||= {}
|
549
558
|
@case_insensitive_cache[column.sql_type] ||= begin
|
550
|
-
sql =
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
559
|
+
sql = <<~SQL
|
560
|
+
SELECT exists(
|
561
|
+
SELECT * FROM pg_proc
|
562
|
+
WHERE proname = 'lower'
|
563
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
564
|
+
) OR exists(
|
565
|
+
SELECT * FROM pg_proc
|
566
|
+
INNER JOIN pg_cast
|
567
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
568
|
+
WHERE proname = 'lower'
|
569
|
+
AND castsource = #{quote column.sql_type}::regtype
|
570
|
+
)
|
571
|
+
SQL
|
563
572
|
select_value(sql, 'SCHEMA')
|
564
573
|
end
|
565
574
|
end
|
566
575
|
|
567
|
-
def translate_exception(exception, message)
|
576
|
+
def translate_exception(exception, message:, sql:, binds:)
|
568
577
|
return super unless exception.is_a?(ActiveRecord::JDBCError)
|
569
578
|
|
570
579
|
# TODO: Can we base these on an error code of some kind?
|
571
580
|
case exception.message
|
572
581
|
when /duplicate key value violates unique constraint/
|
573
|
-
::ActiveRecord::RecordNotUnique.new(message)
|
582
|
+
::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
|
574
583
|
when /violates not-null constraint/
|
575
|
-
::ActiveRecord::NotNullViolation.new(message)
|
584
|
+
::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
576
585
|
when /violates foreign key constraint/
|
577
|
-
::ActiveRecord::InvalidForeignKey.new(message)
|
586
|
+
::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
|
578
587
|
when /value too long/
|
579
|
-
::ActiveRecord::ValueTooLong.new(message)
|
588
|
+
::ActiveRecord::ValueTooLong.new(message, sql: sql, binds: binds)
|
580
589
|
when /out of range/
|
581
|
-
::ActiveRecord::RangeError.new(message)
|
590
|
+
::ActiveRecord::RangeError.new(message, sql: sql, binds: binds)
|
582
591
|
when /could not serialize/
|
583
|
-
::ActiveRecord::SerializationFailure.new(message)
|
592
|
+
::ActiveRecord::SerializationFailure.new(message, sql: sql, binds: binds)
|
584
593
|
when /deadlock detected/
|
585
|
-
::ActiveRecord::Deadlocked.new(message)
|
594
|
+
::ActiveRecord::Deadlocked.new(message, sql: sql, binds: binds)
|
586
595
|
when /lock timeout/
|
587
|
-
::ActiveRecord::LockWaitTimeout.new(message)
|
596
|
+
::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
|
588
597
|
when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
|
589
|
-
::ActiveRecord::QueryCanceled.new(message)
|
598
|
+
::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
|
590
599
|
else
|
591
600
|
super
|
592
601
|
end
|
@@ -610,11 +619,6 @@ module ArJdbc
|
|
610
619
|
end
|
611
620
|
end
|
612
621
|
|
613
|
-
def extract_table_ref_from_insert_sql(sql)
|
614
|
-
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
615
|
-
$1.strip if $1
|
616
|
-
end
|
617
|
-
|
618
622
|
def local_tz
|
619
623
|
@local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
620
624
|
end
|
@@ -635,6 +639,7 @@ module ActiveRecord::ConnectionAdapters
|
|
635
639
|
remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
|
636
640
|
|
637
641
|
class PostgreSQLAdapter < AbstractAdapter
|
642
|
+
class_attribute :create_unlogged_tables, default: false
|
638
643
|
|
639
644
|
# Try to use as much of the built in postgres logic as possible
|
640
645
|
# maybe someday we can extend the actual adapter
|
@@ -672,11 +677,16 @@ module ActiveRecord::ConnectionAdapters
|
|
672
677
|
initialize_type_map
|
673
678
|
|
674
679
|
@use_insert_returning = @config.key?(:insert_returning) ?
|
675
|
-
self.class.type_cast_config_to_boolean(@config[:insert_returning]) :
|
680
|
+
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
676
681
|
end
|
677
682
|
|
678
|
-
def
|
679
|
-
|
683
|
+
def self.database_exists?(config)
|
684
|
+
conn = ActiveRecord::Base.postgresql_connection(config)
|
685
|
+
conn && conn.really_valid?
|
686
|
+
rescue ActiveRecord::NoDatabaseError
|
687
|
+
false
|
688
|
+
ensure
|
689
|
+
conn.disconnect! if conn
|
680
690
|
end
|
681
691
|
|
682
692
|
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
@@ -685,11 +695,8 @@ module ActiveRecord::ConnectionAdapters
|
|
685
695
|
TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
|
686
696
|
Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
|
687
697
|
|
688
|
-
def create_table_definition(*args) # :nodoc:
|
689
|
-
TableDefinition.new(*args)
|
690
|
-
end
|
691
|
-
|
692
698
|
public :sql_for_insert
|
699
|
+
alias :postgresql_version :database_version
|
693
700
|
|
694
701
|
def jdbc_connection_class(spec)
|
695
702
|
::ArJdbc::PostgreSQL.jdbc_connection_class
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This implements a basic encoder to work around ActiveRecord's dependence on the pg gem
|
2
4
|
module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
|
3
5
|
class Array < ActiveModel::Type::Value
|
@@ -6,10 +8,10 @@ module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
|
|
6
8
|
class Array
|
7
9
|
|
8
10
|
def initialize(name:, delimiter:)
|
9
|
-
@type = if name == 'string[]'
|
11
|
+
@type = if name == 'string[]'
|
10
12
|
'text'.freeze
|
11
13
|
else
|
12
|
-
base_type = name.chomp('[]'
|
14
|
+
base_type = name.chomp('[]').to_sym
|
13
15
|
ActiveRecord::Base.connection.native_database_types[base_type][:name]
|
14
16
|
end
|
15
17
|
end
|