activerecord-jdbc-alt-adapter 52.5.1-java → 60.2.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 +2 -0
- data/.nvimlog +0 -0
- data/.travis.yml +61 -37
- data/Gemfile +10 -3
- data/README.md +44 -28
- data/Rakefile +1 -1
- data/Rakefile.jdbc +8 -1
- data/activerecord-jdbc-adapter.gemspec +5 -8
- data/activerecord-jdbc-alt-adapter.gemspec +5 -8
- data/lib/arel/visitors/sqlserver.rb +33 -23
- data/lib/arjdbc/abstract/connection_management.rb +7 -0
- data/lib/arjdbc/abstract/core.rb +16 -23
- data/lib/arjdbc/abstract/database_statements.rb +24 -0
- 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/mssql/adapter.rb +105 -36
- data/lib/arjdbc/mssql/column.rb +5 -1
- data/lib/arjdbc/mssql/connection_methods.rb +8 -2
- data/lib/arjdbc/mssql/database_limits.rb +2 -0
- data/lib/arjdbc/mssql/database_statements.rb +43 -5
- data/lib/arjdbc/mssql/errors.rb +2 -0
- data/lib/arjdbc/mssql/explain_support.rb +3 -1
- data/lib/arjdbc/mssql/extensions/attribute_methods.rb +5 -1
- data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
- data/lib/arjdbc/mssql/quoting.rb +38 -0
- data/lib/arjdbc/mssql/schema_creation.rb +24 -2
- data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
- data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
- data/lib/arjdbc/mssql/schema_statements.rb +63 -21
- data/lib/arjdbc/mssql/transaction.rb +2 -0
- data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
- data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
- data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
- data/lib/arjdbc/mssql/types/string_types.rb +2 -0
- data/lib/arjdbc/mssql/types.rb +2 -0
- data/lib/arjdbc/mssql/utils.rb +2 -0
- data/lib/arjdbc/mssql.rb +3 -1
- data/lib/arjdbc/mysql/adapter.rb +47 -18
- data/lib/arjdbc/postgresql/adapter.rb +240 -214
- 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/name.rb +2 -0
- data/lib/arjdbc/postgresql/oid_types.rb +3 -1
- data/lib/arjdbc/sqlite3/adapter.rb +188 -180
- data/lib/arjdbc/sqlite3/connection_methods.rb +15 -4
- data/lib/arjdbc/tasks/databases.rake +13 -10
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
- 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/src/java/arjdbc/ArJdbcModule.java +5 -5
- data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +406 -629
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +56 -30
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
- data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
- metadata +7 -16
@@ -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,118 @@ 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_common_table_expressions?
|
307
|
+
true
|
308
|
+
end
|
309
|
+
|
310
|
+
def supports_lazy_transactions?
|
311
|
+
true
|
302
312
|
end
|
303
313
|
|
304
314
|
# From AR 5.1 postgres_adapter.rb
|
@@ -306,65 +316,60 @@ module ArJdbc
|
|
306
316
|
index.using == :btree || super
|
307
317
|
end
|
308
318
|
|
319
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
320
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
321
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
322
|
+
end
|
323
|
+
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
|
324
|
+
end
|
325
|
+
|
326
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
327
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
328
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
329
|
+
end
|
330
|
+
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
331
|
+
end
|
332
|
+
|
309
333
|
def enable_extension(name)
|
310
|
-
|
334
|
+
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
|
335
|
+
reload_type_map
|
336
|
+
}
|
311
337
|
end
|
312
338
|
|
313
339
|
def disable_extension(name)
|
314
|
-
|
340
|
+
exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
|
341
|
+
reload_type_map
|
342
|
+
}
|
343
|
+
end
|
344
|
+
|
345
|
+
def extension_available?(name)
|
346
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
315
347
|
end
|
316
348
|
|
317
349
|
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
|
350
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
323
351
|
end
|
324
352
|
|
325
353
|
def extensions
|
326
|
-
|
327
|
-
rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
|
328
|
-
rows.map { |row| row.first }
|
329
|
-
else
|
330
|
-
[]
|
331
|
-
end
|
354
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
332
355
|
end
|
333
356
|
|
334
|
-
|
335
|
-
|
357
|
+
# Returns the configured supported identifier length supported by PostgreSQL
|
358
|
+
def max_identifier_length
|
359
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
336
360
|
end
|
337
361
|
|
338
|
-
# Set the authorized user for this session
|
362
|
+
# Set the authorized user for this session
|
339
363
|
def session_auth=(user)
|
340
364
|
clear_cache!
|
341
|
-
execute
|
342
|
-
end
|
343
|
-
|
344
|
-
# Came from postgres_adapter
|
345
|
-
def get_advisory_lock(lock_id) # :nodoc:
|
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});")
|
365
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
350
366
|
end
|
351
367
|
|
352
|
-
|
353
|
-
|
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
|
368
|
+
def use_insert_returning?
|
369
|
+
@use_insert_returning
|
363
370
|
end
|
364
|
-
alias table_alias_length max_identifier_length
|
365
|
-
alias index_name_length max_identifier_length
|
366
371
|
|
367
|
-
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
372
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
368
373
|
val = super
|
369
374
|
if !use_insert_returning? && pk
|
370
375
|
unless sequence_name
|
@@ -378,25 +383,27 @@ module ArJdbc
|
|
378
383
|
end
|
379
384
|
end
|
380
385
|
|
386
|
+
def execute_batch(statements, name = nil)
|
387
|
+
if statements.is_a? Array
|
388
|
+
execute(combine_multi_statements(statements), name)
|
389
|
+
else
|
390
|
+
execute(statements, name)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
381
394
|
def explain(arel, binds = [])
|
382
395
|
sql, binds = to_sql_and_binds(arel, binds)
|
383
396
|
ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
|
384
397
|
end
|
385
398
|
|
386
|
-
#
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
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
|
399
|
+
# from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
|
400
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
401
|
+
:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
|
402
|
+
) # :nodoc:
|
403
|
+
private_constant :READ_QUERY
|
404
|
+
|
405
|
+
def write_query?(sql) # :nodoc:
|
406
|
+
!READ_QUERY.match?(sql)
|
400
407
|
end
|
401
408
|
|
402
409
|
# We need to make sure to deallocate all the prepared statements
|
@@ -427,6 +434,29 @@ module ArJdbc
|
|
427
434
|
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
|
428
435
|
end
|
429
436
|
|
437
|
+
def build_insert_sql(insert) # :nodoc:
|
438
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
439
|
+
|
440
|
+
if insert.skip_duplicates?
|
441
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
442
|
+
elsif insert.update_duplicates?
|
443
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
444
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
445
|
+
end
|
446
|
+
|
447
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
448
|
+
sql
|
449
|
+
end
|
450
|
+
|
451
|
+
def build_truncate_statements(*table_names)
|
452
|
+
["TRUNCATE TABLE #{table_names.flatten.map(&method(:quote_table_name)).join(", ")}"]
|
453
|
+
end
|
454
|
+
|
455
|
+
def truncate(table_name, name = nil)
|
456
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
|
457
|
+
execute("TRUNCATE TABLE #{quote_table_name(table_name)}", name)
|
458
|
+
end
|
459
|
+
|
430
460
|
def all_schemas
|
431
461
|
select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
|
432
462
|
end
|
@@ -463,13 +493,7 @@ module ArJdbc
|
|
463
493
|
|
464
494
|
def escape_bytea(string)
|
465
495
|
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
|
496
|
+
"\\x#{string.unpack("H*")[0]}"
|
473
497
|
end
|
474
498
|
|
475
499
|
# @override
|
@@ -488,7 +512,7 @@ module ArJdbc
|
|
488
512
|
alias_method :quote_schema_name, :quote_column_name
|
489
513
|
|
490
514
|
# Need to clear the cache even though the AR adapter doesn't for some reason
|
491
|
-
def remove_column(table_name, column_name, type = nil, options
|
515
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
492
516
|
super
|
493
517
|
clear_cache!
|
494
518
|
end
|
@@ -502,6 +526,19 @@ module ArJdbc
|
|
502
526
|
nil
|
503
527
|
end
|
504
528
|
|
529
|
+
|
530
|
+
# @private
|
531
|
+
def column_name_for_operation(operation, node)
|
532
|
+
case operation
|
533
|
+
when 'maximum' then 'max'
|
534
|
+
when 'minimum' then 'min'
|
535
|
+
when 'average' then 'avg'
|
536
|
+
else operation.downcase
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
private
|
541
|
+
|
505
542
|
# Returns the list of a table's column names, data types, and default values.
|
506
543
|
#
|
507
544
|
# If the table name is not prefixed with a schema, the database will
|
@@ -511,82 +548,73 @@ module ArJdbc
|
|
511
548
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
512
549
|
# - ::regclass is a function that gives the id for a table name
|
513
550
|
def column_definitions(table_name)
|
514
|
-
select_rows(
|
551
|
+
select_rows(<<~SQL, 'SCHEMA')
|
515
552
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
516
553
|
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
|
554
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
521
555
|
FROM pg_attribute a
|
522
556
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
557
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
558
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
523
559
|
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
524
560
|
AND a.attnum > 0 AND NOT a.attisdropped
|
525
561
|
ORDER BY a.attnum
|
526
|
-
|
562
|
+
SQL
|
527
563
|
end
|
528
|
-
private :column_definitions
|
529
564
|
|
530
|
-
def
|
531
|
-
|
565
|
+
def extract_table_ref_from_insert_sql(sql)
|
566
|
+
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
567
|
+
$1.strip if $1
|
532
568
|
end
|
533
569
|
|
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
|
570
|
+
def arel_visitor
|
571
|
+
Arel::Visitors::PostgreSQL.new(self)
|
542
572
|
end
|
543
573
|
|
544
|
-
private
|
545
|
-
|
546
574
|
# Pulled from ActiveRecord's Postgres adapter and modified to use execute
|
547
575
|
def can_perform_case_insensitive_comparison_for?(column)
|
548
576
|
@case_insensitive_cache ||= {}
|
549
577
|
@case_insensitive_cache[column.sql_type] ||= begin
|
550
|
-
sql =
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
578
|
+
sql = <<~SQL
|
579
|
+
SELECT exists(
|
580
|
+
SELECT * FROM pg_proc
|
581
|
+
WHERE proname = 'lower'
|
582
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
583
|
+
) OR exists(
|
584
|
+
SELECT * FROM pg_proc
|
585
|
+
INNER JOIN pg_cast
|
586
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
587
|
+
WHERE proname = 'lower'
|
588
|
+
AND castsource = #{quote column.sql_type}::regtype
|
589
|
+
)
|
590
|
+
SQL
|
563
591
|
select_value(sql, 'SCHEMA')
|
564
592
|
end
|
565
593
|
end
|
566
594
|
|
567
|
-
def translate_exception(exception, message)
|
595
|
+
def translate_exception(exception, message:, sql:, binds:)
|
568
596
|
return super unless exception.is_a?(ActiveRecord::JDBCError)
|
569
597
|
|
570
598
|
# TODO: Can we base these on an error code of some kind?
|
571
599
|
case exception.message
|
572
600
|
when /duplicate key value violates unique constraint/
|
573
|
-
::ActiveRecord::RecordNotUnique.new(message)
|
601
|
+
::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
|
574
602
|
when /violates not-null constraint/
|
575
|
-
::ActiveRecord::NotNullViolation.new(message)
|
603
|
+
::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
576
604
|
when /violates foreign key constraint/
|
577
|
-
::ActiveRecord::InvalidForeignKey.new(message)
|
605
|
+
::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
|
578
606
|
when /value too long/
|
579
|
-
::ActiveRecord::ValueTooLong.new(message)
|
607
|
+
::ActiveRecord::ValueTooLong.new(message, sql: sql, binds: binds)
|
580
608
|
when /out of range/
|
581
|
-
::ActiveRecord::RangeError.new(message)
|
609
|
+
::ActiveRecord::RangeError.new(message, sql: sql, binds: binds)
|
582
610
|
when /could not serialize/
|
583
|
-
::ActiveRecord::SerializationFailure.new(message)
|
611
|
+
::ActiveRecord::SerializationFailure.new(message, sql: sql, binds: binds)
|
584
612
|
when /deadlock detected/
|
585
|
-
::ActiveRecord::Deadlocked.new(message)
|
613
|
+
::ActiveRecord::Deadlocked.new(message, sql: sql, binds: binds)
|
586
614
|
when /lock timeout/
|
587
|
-
::ActiveRecord::LockWaitTimeout.new(message)
|
615
|
+
::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
|
588
616
|
when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
|
589
|
-
::ActiveRecord::QueryCanceled.new(message)
|
617
|
+
::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
|
590
618
|
else
|
591
619
|
super
|
592
620
|
end
|
@@ -610,11 +638,6 @@ module ArJdbc
|
|
610
638
|
end
|
611
639
|
end
|
612
640
|
|
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
641
|
def local_tz
|
619
642
|
@local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
620
643
|
end
|
@@ -635,6 +658,7 @@ module ActiveRecord::ConnectionAdapters
|
|
635
658
|
remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
|
636
659
|
|
637
660
|
class PostgreSQLAdapter < AbstractAdapter
|
661
|
+
class_attribute :create_unlogged_tables, default: false
|
638
662
|
|
639
663
|
# Try to use as much of the built in postgres logic as possible
|
640
664
|
# maybe someday we can extend the actual adapter
|
@@ -672,11 +696,16 @@ module ActiveRecord::ConnectionAdapters
|
|
672
696
|
initialize_type_map
|
673
697
|
|
674
698
|
@use_insert_returning = @config.key?(:insert_returning) ?
|
675
|
-
self.class.type_cast_config_to_boolean(@config[:insert_returning]) :
|
699
|
+
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
676
700
|
end
|
677
701
|
|
678
|
-
def
|
679
|
-
|
702
|
+
def self.database_exists?(config)
|
703
|
+
conn = ActiveRecord::Base.postgresql_connection(config)
|
704
|
+
conn && conn.really_valid?
|
705
|
+
rescue ActiveRecord::NoDatabaseError
|
706
|
+
false
|
707
|
+
ensure
|
708
|
+
conn.disconnect! if conn
|
680
709
|
end
|
681
710
|
|
682
711
|
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
@@ -685,11 +714,8 @@ module ActiveRecord::ConnectionAdapters
|
|
685
714
|
TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
|
686
715
|
Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
|
687
716
|
|
688
|
-
def create_table_definition(*args) # :nodoc:
|
689
|
-
TableDefinition.new(*args)
|
690
|
-
end
|
691
|
-
|
692
717
|
public :sql_for_insert
|
718
|
+
alias :postgresql_version :database_version
|
693
719
|
|
694
720
|
def jdbc_connection_class(spec)
|
695
721
|
::ArJdbc::PostgreSQL.jdbc_connection_class
|