activerecord-jdbc-alt-adapter 52.6.0-java → 60.0.0-java
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 +4 -4
- data/.gitignore +0 -2
- data/.travis.yml +58 -37
- data/Gemfile +9 -2
- data/README.md +30 -14
- 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_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.rb +3 -1
- 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.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/utils.rb +2 -0
- data/lib/arjdbc/mysql/adapter.rb +47 -18
- 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/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 -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 +37 -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
- data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
- metadata +8 -16
data/lib/arjdbc/mssql/quoting.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -64,6 +66,42 @@ module ActiveRecord
|
|
|
64
66
|
# @see #quote in old adapter
|
|
65
67
|
BLOB_VALUE_MARKER = "''"
|
|
66
68
|
|
|
69
|
+
def column_name_matcher
|
|
70
|
+
COLUMN_NAME
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def column_name_with_order_matcher
|
|
74
|
+
COLUMN_NAME_WITH_ORDER
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
COLUMN_NAME = /
|
|
78
|
+
\A
|
|
79
|
+
(
|
|
80
|
+
(?:
|
|
81
|
+
# \[table_name\].\[column_name\] | function(one or no argument)
|
|
82
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
|
83
|
+
)
|
|
84
|
+
(?:\s+AS\s+(?:\w+|\[\w+\]))?
|
|
85
|
+
)
|
|
86
|
+
(?:\s*,\s*\g<1>)*
|
|
87
|
+
\z
|
|
88
|
+
/ix
|
|
89
|
+
|
|
90
|
+
COLUMN_NAME_WITH_ORDER = /
|
|
91
|
+
\A
|
|
92
|
+
(
|
|
93
|
+
(?:
|
|
94
|
+
# \[table_name\].\[column_name\] | function(one or no argument)
|
|
95
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
|
96
|
+
)
|
|
97
|
+
(?:\s+ASC|\s+DESC)?
|
|
98
|
+
)
|
|
99
|
+
(?:\s*,\s*\g<1>)*
|
|
100
|
+
\z
|
|
101
|
+
/ix
|
|
102
|
+
|
|
103
|
+
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
104
|
+
|
|
67
105
|
private
|
|
68
106
|
|
|
69
107
|
def time_with_db_timezone(value)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -11,8 +13,28 @@ module ActiveRecord
|
|
|
11
13
|
projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
|
|
12
14
|
select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
|
|
13
15
|
else
|
|
14
|
-
o.instance_variable_set :@as, nil
|
|
15
|
-
super
|
|
16
|
+
# o.instance_variable_set :@as, nil
|
|
17
|
+
# super
|
|
18
|
+
create_sql = +''
|
|
19
|
+
|
|
20
|
+
create_sql << "IF NOT EXISTS (SELECT 1 FROM sysobjects WHERE name='#{o.name}' and xtype='U') " if o.if_not_exists
|
|
21
|
+
create_sql << "CREATE#{table_modifier_in_create(o)} TABLE "
|
|
22
|
+
create_sql << "#{quote_table_name(o.name)} "
|
|
23
|
+
|
|
24
|
+
statements = o.columns.map { |c| accept c }
|
|
25
|
+
statements << accept(o.primary_keys) if o.primary_keys
|
|
26
|
+
|
|
27
|
+
if supports_indexes_in_create?
|
|
28
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if supports_foreign_keys?
|
|
32
|
+
statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
create_sql << "(#{statements.join(', ')})" if statements.present?
|
|
36
|
+
add_table_options!(create_sql, table_options(o))
|
|
37
|
+
create_sql
|
|
16
38
|
end
|
|
17
39
|
end
|
|
18
40
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -81,6 +83,14 @@ module ActiveRecord
|
|
|
81
83
|
|
|
82
84
|
super
|
|
83
85
|
end
|
|
86
|
+
|
|
87
|
+
def timestamps(**options)
|
|
88
|
+
if !options.key?(:precision) && @conn.supports_datetime_with_precision?
|
|
89
|
+
options[:precision] = 7
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
super
|
|
93
|
+
end
|
|
84
94
|
end
|
|
85
95
|
|
|
86
96
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
4
|
-
module SchemaStatements
|
|
6
|
+
module SchemaStatements # :nodoc:
|
|
5
7
|
|
|
6
8
|
NATIVE_DATABASE_TYPES = {
|
|
7
9
|
# Logical Rails types to SQL Server types
|
|
@@ -41,23 +43,36 @@ module ActiveRecord
|
|
|
41
43
|
NATIVE_DATABASE_TYPES
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
|
45
|
-
# See the concrete implementation for details on the expected parameter values.
|
|
46
|
-
# NOTE: This is ready, all implemented in the java part of adapter,
|
|
47
|
-
# it uses MSSQLColumn, SqlTypeMetadata, etc.
|
|
48
|
-
def columns(table_name)
|
|
49
|
-
log('JDBC: GETCOLUMNS', 'SCHEMA') { @connection.columns(table_name) }
|
|
50
|
-
rescue => e
|
|
51
|
-
# raise translate_exception_class(e, nil)
|
|
52
|
-
# FIXME: this breaks one arjdbc test but fixes activerecord tests
|
|
53
|
-
# (table name alias). Also it behaves similarly to the CRuby adapter
|
|
54
|
-
# which returns an empty array too. (postgres throws a exception)
|
|
55
|
-
[]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
46
|
# Returns an array of indexes for the given table.
|
|
59
|
-
def indexes(table_name
|
|
60
|
-
|
|
47
|
+
def indexes(table_name)
|
|
48
|
+
data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue []
|
|
49
|
+
|
|
50
|
+
data.reduce([]) do |indexes, index|
|
|
51
|
+
index = index.with_indifferent_access
|
|
52
|
+
|
|
53
|
+
if index[:index_description] =~ /primary key/
|
|
54
|
+
indexes
|
|
55
|
+
else
|
|
56
|
+
name = index[:index_name]
|
|
57
|
+
unique = index[:index_description].to_s.match?(/unique/)
|
|
58
|
+
where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}")
|
|
59
|
+
orders = {}
|
|
60
|
+
columns = []
|
|
61
|
+
|
|
62
|
+
index[:index_keys].split(',').each do |column|
|
|
63
|
+
column.strip!
|
|
64
|
+
|
|
65
|
+
if column.ends_with?('(-)')
|
|
66
|
+
column.gsub! '(-)', ''
|
|
67
|
+
orders[column] = :desc
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
columns << column
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
61
76
|
end
|
|
62
77
|
|
|
63
78
|
def primary_keys(table_name)
|
|
@@ -124,7 +139,7 @@ module ActiveRecord
|
|
|
124
139
|
end
|
|
125
140
|
end
|
|
126
141
|
|
|
127
|
-
if options[:if_exists] &&
|
|
142
|
+
if options[:if_exists] && mssql_major_version < 13
|
|
128
143
|
# this is for sql server 2012 and 2014
|
|
129
144
|
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
|
|
130
145
|
else
|
|
@@ -193,7 +208,7 @@ module ActiveRecord
|
|
|
193
208
|
column_type_sql << "(#{precision})"
|
|
194
209
|
else
|
|
195
210
|
raise(
|
|
196
|
-
|
|
211
|
+
ArgumentError,
|
|
197
212
|
"No #{native[:name]} type has precision of #{precision}. The " \
|
|
198
213
|
'allowed range of precision is from 0 to 7, even though the ' \
|
|
199
214
|
'sql type precision is 7 this adapter will persist up to 6 ' \
|
|
@@ -220,6 +235,14 @@ module ActiveRecord
|
|
|
220
235
|
(order_columns << super).join(', ')
|
|
221
236
|
end
|
|
222
237
|
|
|
238
|
+
def add_timestamps(table_name, options = {})
|
|
239
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
240
|
+
options[:precision] = 7
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
super
|
|
244
|
+
end
|
|
245
|
+
|
|
223
246
|
def create_schema_dumper(options)
|
|
224
247
|
MSSQL::SchemaDumper.create(self, options)
|
|
225
248
|
end
|
|
@@ -299,13 +322,30 @@ module ActiveRecord
|
|
|
299
322
|
execute(sql_alter.join(' '))
|
|
300
323
|
end
|
|
301
324
|
|
|
325
|
+
def update_table_definition(table_name, base) #:nodoc:
|
|
326
|
+
MSSQL::Table.new(table_name, base)
|
|
327
|
+
end
|
|
328
|
+
|
|
302
329
|
private
|
|
303
330
|
|
|
331
|
+
def schema_creation
|
|
332
|
+
MSSQL::SchemaCreation.new(self)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def create_table_definition(*args)
|
|
336
|
+
MSSQL::TableDefinition.new(self, *args)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def new_column_from_field(table_name, field)
|
|
340
|
+
field
|
|
341
|
+
end
|
|
342
|
+
|
|
304
343
|
def data_source_sql(name = nil, type: nil)
|
|
305
344
|
scope = quoted_scope(name, type: type)
|
|
306
345
|
table_name = 'TABLE_NAME'
|
|
307
346
|
|
|
308
|
-
sql =
|
|
347
|
+
sql = ''.dup
|
|
348
|
+
sql << "SELECT #{table_name}"
|
|
309
349
|
sql << ' FROM INFORMATION_SCHEMA.TABLES'
|
|
310
350
|
sql << ' WHERE TABLE_CATALOG = DB_NAME()'
|
|
311
351
|
sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
|
@@ -327,7 +367,9 @@ module ActiveRecord
|
|
|
327
367
|
end
|
|
328
368
|
|
|
329
369
|
def change_column_type(table_name, column_name, type, options = {})
|
|
330
|
-
sql =
|
|
370
|
+
sql = ''.dup
|
|
371
|
+
|
|
372
|
+
sql << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
|
|
331
373
|
sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
|
|
332
374
|
result = execute(sql)
|
|
333
375
|
result
|
data/lib/arjdbc/mssql/types.rb
CHANGED
data/lib/arjdbc/mssql/utils.rb
CHANGED
data/lib/arjdbc/mysql/adapter.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
ArJdbc.load_java_part :MySQL
|
|
2
4
|
|
|
3
5
|
require 'bigdecimal'
|
|
@@ -19,7 +21,7 @@ module ActiveRecord
|
|
|
19
21
|
remove_const(:Mysql2Adapter) if const_defined?(:Mysql2Adapter)
|
|
20
22
|
|
|
21
23
|
class Mysql2Adapter < AbstractMysqlAdapter
|
|
22
|
-
ADAPTER_NAME = 'Mysql2'
|
|
24
|
+
ADAPTER_NAME = 'Mysql2'
|
|
23
25
|
|
|
24
26
|
include Jdbc::ConnectionPoolCallbacks
|
|
25
27
|
|
|
@@ -32,21 +34,30 @@ module ActiveRecord
|
|
|
32
34
|
include ArJdbc::MySQL
|
|
33
35
|
|
|
34
36
|
def initialize(connection, logger, connection_parameters, config)
|
|
35
|
-
# workaround to skip version check on JNDI to be lazy, dummy version is high enough for Rails 5.0 - 6.0
|
|
36
|
-
is_jndi = ::ActiveRecord::ConnectionAdapters::JdbcConnection.jndi_config?(config)
|
|
37
|
-
@version = '8.1.5' if is_jndi
|
|
38
|
-
|
|
39
37
|
super
|
|
40
38
|
|
|
41
|
-
# set to nil to have it lazy-load the real value when required
|
|
42
|
-
@version = nil if is_jndi
|
|
43
|
-
|
|
44
39
|
@prepared_statements = false unless config.key?(:prepared_statements)
|
|
45
40
|
# configure_connection taken care of at ArJdbc::Abstract::Core
|
|
46
41
|
end
|
|
47
42
|
|
|
43
|
+
def self.database_exists?(config)
|
|
44
|
+
conn = ActiveRecord::Base.mysql2_connection(config)
|
|
45
|
+
conn && conn.really_valid?
|
|
46
|
+
rescue ActiveRecord::NoDatabaseError
|
|
47
|
+
false
|
|
48
|
+
ensure
|
|
49
|
+
conn.disconnect! if conn
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def check_version
|
|
53
|
+
# for JNDI, don't check version as the whole connection should be lazy
|
|
54
|
+
return if ::ActiveRecord::ConnectionAdapters::JdbcConnection.jndi_config?(config)
|
|
55
|
+
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
def supports_json?
|
|
49
|
-
!mariadb? &&
|
|
60
|
+
!mariadb? && database_version >= '5.7.8'
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
def supports_comments?
|
|
@@ -61,6 +72,10 @@ module ActiveRecord
|
|
|
61
72
|
true
|
|
62
73
|
end
|
|
63
74
|
|
|
75
|
+
def supports_lazy_transactions?
|
|
76
|
+
true
|
|
77
|
+
end
|
|
78
|
+
|
|
64
79
|
def supports_transaction_isolation?
|
|
65
80
|
true
|
|
66
81
|
end
|
|
@@ -71,6 +86,16 @@ module ActiveRecord
|
|
|
71
86
|
|
|
72
87
|
# HELPER METHODS ===========================================
|
|
73
88
|
|
|
89
|
+
# from MySQL::DatabaseStatements
|
|
90
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
|
91
|
+
:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :describe, :desc
|
|
92
|
+
) # :nodoc:
|
|
93
|
+
private_constant :READ_QUERY
|
|
94
|
+
|
|
95
|
+
def write_query?(sql) # :nodoc:
|
|
96
|
+
!READ_QUERY.match?(sql)
|
|
97
|
+
end
|
|
98
|
+
|
|
74
99
|
# Reloading the type map in abstract/statement_cache.rb blows up postgres
|
|
75
100
|
def clear_cache!
|
|
76
101
|
reload_type_map
|
|
@@ -136,7 +161,13 @@ module ActiveRecord
|
|
|
136
161
|
private
|
|
137
162
|
|
|
138
163
|
# e.g. "5.7.20-0ubuntu0.16.04.1"
|
|
139
|
-
def full_version
|
|
164
|
+
def full_version
|
|
165
|
+
schema_cache.database_version.full_version_string
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def get_full_version
|
|
169
|
+
@full_version ||= @connection.full_version
|
|
170
|
+
end
|
|
140
171
|
|
|
141
172
|
def jdbc_connection_class(spec)
|
|
142
173
|
::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
|
|
@@ -152,15 +183,13 @@ module ActiveRecord
|
|
|
152
183
|
end
|
|
153
184
|
|
|
154
185
|
# FIXME: optimize insert_fixtures_set by using JDBC Statement.addBatch()/executeBatch()
|
|
155
|
-
def combine_multi_statements(total_sql)
|
|
156
|
-
total_sql
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def with_multi_statements
|
|
160
|
-
yield
|
|
161
|
-
end
|
|
162
186
|
|
|
163
|
-
def
|
|
187
|
+
def combine_multi_statements(total_sql)
|
|
188
|
+
if total_sql.length == 1
|
|
189
|
+
total_sql.first
|
|
190
|
+
else
|
|
191
|
+
total_sql
|
|
192
|
+
end
|
|
164
193
|
end
|
|
165
194
|
end
|
|
166
195
|
end
|
|
@@ -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
|