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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.travis.yml +58 -37
  4. data/Gemfile +9 -2
  5. data/README.md +30 -14
  6. data/Rakefile +1 -1
  7. data/Rakefile.jdbc +8 -1
  8. data/activerecord-jdbc-adapter.gemspec +5 -8
  9. data/activerecord-jdbc-alt-adapter.gemspec +5 -8
  10. data/lib/arel/visitors/sqlserver.rb +33 -23
  11. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  12. data/lib/arjdbc/abstract/core.rb +16 -23
  13. data/lib/arjdbc/abstract/database_statements.rb +24 -0
  14. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  15. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  16. data/lib/arjdbc/db2/column.rb +0 -39
  17. data/lib/arjdbc/derby/adapter.rb +1 -20
  18. data/lib/arjdbc/firebird/adapter.rb +0 -21
  19. data/lib/arjdbc/h2/adapter.rb +0 -15
  20. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  21. data/lib/arjdbc/informix/adapter.rb +0 -23
  22. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  23. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  24. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  25. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  26. data/lib/arjdbc/jdbc/column.rb +2 -0
  27. data/lib/arjdbc/jdbc/connection.rb +2 -0
  28. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  29. data/lib/arjdbc/jdbc/error.rb +2 -0
  30. data/lib/arjdbc/jdbc/extension.rb +2 -0
  31. data/lib/arjdbc/jdbc/java.rb +3 -1
  32. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  33. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  34. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  35. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  36. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  37. data/lib/arjdbc/mssql.rb +3 -1
  38. data/lib/arjdbc/mssql/adapter.rb +105 -36
  39. data/lib/arjdbc/mssql/column.rb +5 -1
  40. data/lib/arjdbc/mssql/connection_methods.rb +8 -2
  41. data/lib/arjdbc/mssql/database_limits.rb +2 -0
  42. data/lib/arjdbc/mssql/database_statements.rb +43 -5
  43. data/lib/arjdbc/mssql/errors.rb +2 -0
  44. data/lib/arjdbc/mssql/explain_support.rb +3 -1
  45. data/lib/arjdbc/mssql/extensions/attribute_methods.rb +5 -1
  46. data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
  47. data/lib/arjdbc/mssql/quoting.rb +38 -0
  48. data/lib/arjdbc/mssql/schema_creation.rb +24 -2
  49. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  50. data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
  51. data/lib/arjdbc/mssql/schema_statements.rb +63 -21
  52. data/lib/arjdbc/mssql/transaction.rb +2 -0
  53. data/lib/arjdbc/mssql/types.rb +2 -0
  54. data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
  55. data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
  56. data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
  57. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
  58. data/lib/arjdbc/mssql/types/string_types.rb +2 -0
  59. data/lib/arjdbc/mssql/utils.rb +2 -0
  60. data/lib/arjdbc/mysql/adapter.rb +47 -18
  61. data/lib/arjdbc/postgresql/adapter.rb +220 -213
  62. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  63. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  64. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  65. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  66. data/lib/arjdbc/postgresql/column.rb +6 -4
  67. data/lib/arjdbc/postgresql/name.rb +2 -0
  68. data/lib/arjdbc/postgresql/oid_types.rb +2 -0
  69. data/lib/arjdbc/sqlite3/adapter.rb +175 -180
  70. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -4
  71. data/lib/arjdbc/tasks/databases.rake +13 -10
  72. data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
  73. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  74. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  75. data/lib/arjdbc/util/table_copier.rb +3 -1
  76. data/lib/arjdbc/version.rb +1 -1
  77. data/pom.xml +4 -4
  78. data/rakelib/01-tomcat.rake +2 -2
  79. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  80. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  81. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +406 -629
  82. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +37 -51
  83. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  84. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +31 -24
  85. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  86. data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
  87. metadata +8 -16
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/relation'
2
4
  require 'active_record/version'
3
5
 
@@ -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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module MSSQL
@@ -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, name = nil)
60
- @connection.indexes(table_name, name)
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] && @mssql_major_version < 13
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
- ActiveRecordError,
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 = "SELECT #{table_name}"
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 = "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])}"
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/connection_adapters/abstract/transaction'
2
4
 
3
5
  # MSSQL doe not restore the initial transaction isolation when the transaction
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'arjdbc/mssql/types/numeric_types'
2
4
  require 'arjdbc/mssql/types/string_types'
3
5
  require 'arjdbc/mssql/types/binary_types'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MSSQL binary types definitions
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MSSQL date and time types definitions
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MSSQL deprecated type definitions
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MSSQL numeric types definitions
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MSSQL string types definitions
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # NOTE: file contains code adapted from **sqlserver** adapter, license follows
2
4
  =begin
3
5
  Copyright (c) 2008-2015
@@ -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'.freeze
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? && version >= '5.7.8'
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; @full_version ||= @connection.full_version end
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 discard_remaining_results
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: false
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'.freeze
45
+ ADAPTER_NAME = 'PostgreSQL'
46
46
 
47
47
  def adapter_name
48
48
  ADAPTER_NAME
49
49
  end
50
50
 
51
- def postgresql_version
52
- @postgresql_version ||=
53
- begin
54
- version = @connection.database_product
55
- if match = version.match(/([\d\.]*\d).*?/)
56
- version = match[1].split('.').map(&:to_i)
57
- # PostgreSQL version representation does not have more than 4 digits
58
- # From version 10 onwards, PG has changed its versioning policy to
59
- # limit it to only 2 digits. i.e. in 10.x, 10 being the major
60
- # version and x representing the patch release
61
- # Refer to:
62
- # https://www.postgresql.org/support/versioning/
63
- # https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
64
- # for more info
65
-
66
- if version.size >= 3
67
- (version[0] * 100 + version[1]) * 100 + version[2]
68
- elsif version.size == 2
69
- if version[0] >= 10
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' }, # This doesn't get added to AR's postgres adapter until 5.1 but it fixes broken tests in 5.0 ...
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
- self.standard_conforming_strings=(true)
201
+ execute("SET standard_conforming_strings = on", "SCHEMA")
204
202
  end
205
203
 
206
- # Enable standard-conforming strings if available.
207
- def standard_conforming_strings=(enable)
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 standard_conforming_strings?
222
- if @standard_conforming_strings.nil?
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 supports_ddl_transactions?; true end
212
+ def supports_partial_index?
213
+ true
214
+ end
238
215
 
239
- def supports_advisory_locks?; true end
216
+ def supports_expression_index?
217
+ true
218
+ end
240
219
 
241
- def supports_explain?; true end
220
+ def supports_transaction_isolation?
221
+ true
222
+ end
242
223
 
243
- def supports_expression_index?; true end
224
+ def supports_foreign_keys?
225
+ true
226
+ end
244
227
 
245
- def supports_foreign_keys?; true end
228
+ def supports_validate_constraints?
229
+ true
230
+ end
246
231
 
247
- def supports_validate_constraints?; true end
232
+ def supports_views?
233
+ true
234
+ end
248
235
 
249
- def supports_index_sort_order?; true end
236
+ def supports_datetime_with_precision?
237
+ true
238
+ end
250
239
 
251
- def supports_partial_index?; true end
240
+ def supports_json?
241
+ database_version >= 90200
242
+ end
252
243
 
253
- def supports_savepoints?; true end
244
+ def supports_comments?
245
+ true
246
+ end
254
247
 
255
- def supports_transaction_isolation?; true end
248
+ def supports_savepoints?
249
+ true
250
+ end
256
251
 
257
- def supports_views?; true end
252
+ def supports_insert_returning?
253
+ true
254
+ end
258
255
 
259
- def supports_bulk_alter?; true end
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 supports_datetime_with_precision?; true end
263
+ def index_algorithms
264
+ { concurrently: 'CONCURRENTLY' }
265
+ end
262
266
 
263
- def supports_comments?; true end
267
+ def supports_ddl_transactions?
268
+ true
269
+ end
264
270
 
265
- # Does PostgreSQL support standard conforming strings?
266
- def supports_standard_conforming_strings?
267
- standard_conforming_strings?
268
- @standard_conforming_strings != :unsupported
271
+ def supports_advisory_locks?
272
+ true
269
273
  end
270
274
 
271
- def supports_foreign_tables? # we don't really support this yet, its a reminder :)
272
- postgresql_version >= 90300
275
+ def supports_explain?
276
+ true
273
277
  end
274
278
 
275
- def supports_hex_escaped_bytea?
276
- postgresql_version >= 90000
279
+ def supports_extensions?
280
+ database_version >= 90200
277
281
  end
278
282
 
279
- def supports_materialized_views?
280
- postgresql_version >= 90300
283
+ def supports_ranges?
284
+ database_version >= 90200
281
285
  end
282
286
 
283
- def supports_json?
284
- postgresql_version >= 90200
287
+ def supports_materialized_views?
288
+ database_version >= 90300
285
289
  end
286
290
 
287
- def supports_insert_with_returning?
288
- postgresql_version >= 80200
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
- postgresql_version >= 90400
296
+ database_version >= 90400
293
297
  end
294
298
 
295
- # Range data-types weren't introduced until PostgreSQL 9.2.
296
- def supports_ranges?
297
- postgresql_version >= 90200
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 supports_extensions?
301
- postgresql_version >= 90200
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
- execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
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
- execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
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
- if supports_extensions?
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
- if supports_extensions?
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
- def index_algorithms
335
- { :concurrently => 'CONCURRENTLY' }
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 "SET SESSION AUTHORIZATION #{user}"
361
+ execute("SET SESSION AUTHORIZATION #{user}")
342
362
  end
343
363
 
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});")
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
- # @note Only for "better" AR 4.0 compatibility.
387
- # @private
388
- def query(sql, name = nil)
389
- log(sql, name) do
390
- result = []
391
- @connection.execute_query_raw(sql, []) do |*values|
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
- if supports_hex_escaped_bytea?
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(<<-end_sql, 'SCHEMA')
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
- (SELECT c.collname FROM pg_collation c, pg_type t
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
- end_sql
543
+ SQL
527
544
  end
528
- private :column_definitions
529
545
 
530
- def truncate(table_name, name = nil)
531
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
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
- # @private
535
- def column_name_for_operation(operation, node)
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 = <<-end_sql
551
- SELECT exists(
552
- SELECT * FROM pg_proc
553
- WHERE proname = 'lower'
554
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
555
- ) OR exists(
556
- SELECT * FROM pg_proc
557
- INNER JOIN pg_cast
558
- ON ARRAY[casttarget]::oidvector = proargtypes
559
- WHERE proname = 'lower'
560
- AND castsource = #{quote column.sql_type}::regtype
561
- )
562
- end_sql
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]) : nil
680
+ self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
676
681
  end
677
682
 
678
- def arel_visitor # :nodoc:
679
- Arel::Visitors::PostgreSQL.new(self)
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