activerecord-jdbc-alt-adapter 71.0.0-java → 72.0.0.rc1-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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/activerecord-jdbc-adapter.gemspec +1 -1
  4. data/activerecord-jdbc-alt-adapter.gemspec +1 -1
  5. data/lib/arel/visitors/sqlserver.rb +13 -9
  6. data/lib/arjdbc/abstract/core.rb +10 -1
  7. data/lib/arjdbc/abstract/database_statements.rb +1 -1
  8. data/lib/arjdbc/abstract/transaction_support.rb +3 -1
  9. data/lib/arjdbc/jdbc/adapter.rb +0 -1
  10. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  11. data/lib/arjdbc/mssql/adapter.rb +18 -15
  12. data/lib/arjdbc/mssql/adapter_hash_config.rb +53 -0
  13. data/lib/arjdbc/mssql/column.rb +16 -7
  14. data/lib/arjdbc/mssql/database_statements.rb +7 -2
  15. data/lib/arjdbc/mssql/quoting.rb +56 -36
  16. data/lib/arjdbc/mssql/schema_creation.rb +16 -0
  17. data/lib/arjdbc/mssql/schema_statements.rb +44 -19
  18. data/lib/arjdbc/mssql.rb +1 -1
  19. data/lib/arjdbc/mysql/adapter.rb +8 -11
  20. data/lib/arjdbc/mysql/adapter_hash_config.rb +159 -0
  21. data/lib/arjdbc/mysql.rb +1 -1
  22. data/lib/arjdbc/postgresql/adapter.rb +17 -11
  23. data/lib/arjdbc/postgresql/adapter_hash_config.rb +98 -0
  24. data/lib/arjdbc/postgresql/base/array_encoder.rb +3 -1
  25. data/lib/arjdbc/postgresql/oid_types.rb +2 -2
  26. data/lib/arjdbc/postgresql.rb +1 -1
  27. data/lib/arjdbc/sqlite3/adapter.rb +193 -90
  28. data/lib/arjdbc/sqlite3/adapter_hash_config.rb +91 -0
  29. data/lib/arjdbc/sqlite3/column.rb +17 -3
  30. data/lib/arjdbc/sqlite3/pragmas.rb +105 -0
  31. data/lib/arjdbc/sqlite3.rb +1 -1
  32. data/lib/arjdbc/version.rb +1 -1
  33. data/lib/arjdbc.rb +16 -1
  34. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +5 -0
  35. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +12 -2
  36. metadata +12 -8
  37. data/lib/arjdbc/jdbc/base_ext.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 973f3cfa750c3424b5de9105f177584edea8ebe705d50b20e4b9f6d989a4919f
4
- data.tar.gz: e7f86bfeb49f90f2767e3bdcc3e0101a58149e97421e8d7b96da8f59fc469959
3
+ metadata.gz: 0f7f249466042cd80343c394b99718ff78c5754a2105f06eaf87672f25d2d54e
4
+ data.tar.gz: cc61ff60b8f74d3fa100156f59c4cdd198865e87d9be401bf833a2e0ed623291
5
5
  SHA512:
6
- metadata.gz: f276911281e38f162526b603959f5798fa5d09c73284a79078376126b5fade87add2e4a4345242029a4b4e39097b5a6964651483576f51ed8bee3ac7b6e586a3
7
- data.tar.gz: 8e5d458add630631bfaa82605f20d28c80310e25787c9db2c94c1b8cc677ac8ff6e0730c97b6271a333cbfe919da8a35ad8deb7911aeedc89c3021629e0c5370
6
+ metadata.gz: 25c0368cbbeecad8536caa0ffadff34c28aacb0ff4a66f2085016224322645add68206976dbcef42475bdc39c1ffd47da761eacd919292ae15e6711c44935317
7
+ data.tar.gz: 9beed2af43699a9349a9284c252fd97db3c0110510892ac6937b5ace94df6be02269fb7360caa653fb864a4326d6582e958bea1afe4c69d8adaabec96af2a671
data/README.md CHANGED
@@ -175,7 +175,7 @@ adapters are available:
175
175
 
176
176
  ```yml
177
177
  development:
178
- adapter: mysql2 # or mysql
178
+ adapter: mysql2
179
179
  database: blog_development
180
180
  username: blog
181
181
  password: 1234
@@ -199,7 +199,7 @@ or preferably using the *properties:* syntax:
199
199
 
200
200
  ```yml
201
201
  production:
202
- adapter: mysql
202
+ adapter: mysql2
203
203
  username: blog
204
204
  password: blog
205
205
  url: "jdbc:mysql://localhost:3306/blog?profileSQL=true"
@@ -41,7 +41,7 @@ Gem::Specification.new do |gem|
41
41
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
42
42
  gem.test_files = gem.files.grep(%r{^test/})
43
43
 
44
- gem.add_dependency 'activerecord', '~> 7.1.3'
44
+ gem.add_dependency "activerecord", "~> 7.2.2"
45
45
 
46
46
  #gem.add_development_dependency 'test-unit', '2.5.4'
47
47
  #gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
@@ -43,7 +43,7 @@ Gem::Specification.new do |gem|
43
43
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
44
44
  gem.test_files = gem.files.grep(%r{^test/})
45
45
 
46
- gem.add_dependency 'activerecord', '~> 7.1.3'
46
+ gem.add_dependency "activerecord", "~> 7.2.2"
47
47
 
48
48
  #gem.add_development_dependency 'test-unit', '2.5.4'
49
49
  #gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
@@ -83,15 +83,17 @@ module Arel
83
83
  @select_statement = nil
84
84
  end
85
85
 
86
- def visit_Arel_Table o, collector
86
+ def visit_Arel_Table(o, collector)
87
87
  # Apparently, o.engine.connection can actually be a different adapter
88
88
  # than sqlserver. Can be removed if fixed in ActiveRecord. See:
89
89
  # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
90
90
  table_name = begin
91
- if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server?
92
- remote_server_table_name(o)
93
- else
94
- quote_table_name(o.name)
91
+ o.class.engine.with_connection do |connection|
92
+ if connection.respond_to?(:sqlserver?) && connection.database_prefix_remote_server?
93
+ remote_server_table_name(o)
94
+ else
95
+ quote_table_name(o.name)
96
+ end
95
97
  end
96
98
  rescue Exception => e
97
99
  quote_table_name(o.name)
@@ -259,10 +261,12 @@ module Arel
259
261
  column_name ? t[column_name] : nil
260
262
  end
261
263
 
262
- def remote_server_table_name o
263
- ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
264
- "#{o.class.engine.connection.database_prefix}#{o.name}"
265
- ).quoted
264
+ def remote_server_table_name(o)
265
+ o.class.engine.with_connection do |connection|
266
+ ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
267
+ "#{connection.database_prefix}#{o.name}"
268
+ ).quoted
269
+ end
266
270
  end
267
271
 
268
272
  # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer
@@ -60,7 +60,16 @@ module ArJdbc
60
60
  # this version of log() automatically fills type_casted_binds from binds if necessary
61
61
  def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false)
62
62
  if binds.any? && (type_casted_binds.nil? || type_casted_binds.empty?)
63
- type_casted_binds = ->{ binds.map(&:value_for_database) } # extract_raw_bind_values
63
+ type_casted_binds = lambda {
64
+ # extract_raw_bind_values
65
+ binds.map do |bind|
66
+ if bind.respond_to?(:value_for_database)
67
+ bind.value_for_database
68
+ else
69
+ bind
70
+ end
71
+ end
72
+ }
64
73
  end
65
74
  super
66
75
  end
@@ -79,7 +79,7 @@ module ArJdbc
79
79
  alias :exec_delete :exec_update
80
80
 
81
81
  # overridden to support legacy binds
82
- def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil, async: false)
82
+ def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil, async: false, allow_retry: false)
83
83
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
84
84
  super
85
85
  end
@@ -26,7 +26,9 @@ module ArJdbc
26
26
  def begin_db_transaction
27
27
  log('BEGIN', 'TRANSACTION') do
28
28
  with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
29
- conn.begin
29
+ result = conn.begin
30
+ verified!
31
+ result
30
32
  end
31
33
  end
32
34
  end
@@ -5,7 +5,6 @@ require 'active_record/connection_adapters/abstract_adapter'
5
5
 
6
6
  require 'arjdbc/version'
7
7
  require 'arjdbc/jdbc/java'
8
- require 'arjdbc/jdbc/base_ext'
9
8
  require 'arjdbc/jdbc/error'
10
9
  require 'arjdbc/jdbc/connection_methods'
11
10
  require 'arjdbc/jdbc/column'
Binary file
@@ -30,6 +30,8 @@ require 'arjdbc/mssql/errors'
30
30
  require 'arjdbc/mssql/schema_creation'
31
31
  require 'arjdbc/mssql/database_limits'
32
32
 
33
+ require "arjdbc/mssql/adapter_hash_config"
34
+
33
35
  require "arjdbc/abstract/relation_query_attribute_monkey_patch"
34
36
 
35
37
  module ActiveRecord
@@ -44,6 +46,7 @@ module ActiveRecord
44
46
  # include ArJdbc::Abstract::DatabaseStatements
45
47
  # include ArJdbc::Abstract::StatementCache
46
48
  include ArJdbc::Abstract::TransactionSupport
49
+ include ArJdbc::MSSQLConfig
47
50
 
48
51
  include MSSQL::Quoting
49
52
  include MSSQL::SchemaStatements
@@ -51,6 +54,7 @@ module ActiveRecord
51
54
  include MSSQL::ExplainSupport
52
55
  include MSSQL::DatabaseLimits
53
56
 
57
+ # Latin1-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-sensitive
54
58
  @cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
55
59
 
56
60
  class << self
@@ -81,7 +85,8 @@ module ActiveRecord
81
85
  # configure_connection happens in super
82
86
  super
83
87
 
84
- conn_params = @config.compact
88
+ # assign arjdbc extra connection params
89
+ conn_params = build_connection_config(@config.compact)
85
90
 
86
91
  @raw_connection = nil
87
92
 
@@ -261,29 +266,25 @@ module ActiveRecord
261
266
 
262
267
  alias_method :current_schema=, :default_schema=
263
268
 
264
- # FIXME: This needs to be fixed when we implement the collation per
265
- # column basis. At the moment we only use the global database collation
266
- def default_uniqueness_comparison(attribute, value) # :nodoc:
269
+ # FIXME: This needs to be fixed when we implement the collation per column
270
+ def case_sensitive_comparison(attribute, value)
267
271
  column = column_for_attribute(attribute)
268
272
 
269
- if [:string, :text].include?(column.type) && collation && !collation.match(/_CS/) && !value.nil?
270
- # NOTE: there is a deprecation warning here in the mysql adapter
271
- # no sure if it's required.
273
+ case_sensitive = collation && collation.match(/_CS/)
274
+
275
+ if %i[string text].include?(column.type) && !case_sensitive && !value.nil?
272
276
  attribute.eq(Arel::Nodes::Bin.new(value))
273
277
  else
274
278
  super
275
279
  end
276
280
  end
277
281
 
278
- def case_sensitive_comparison(attribute, value)
279
- column = column_for_attribute(attribute)
282
+ def can_perform_case_insensitive_comparison_for?(column)
283
+ case_sensitive = collation && collation.match(/_CS/)
280
284
 
281
- if [:string, :text].include?(column.type) && collation && !collation.match(/_CS/) && !value.nil?
282
- attribute.eq(Arel::Nodes::Bin.new(value))
283
- else
284
- super
285
- end
285
+ %i[string text].include?(column.type) && !case_sensitive
286
286
  end
287
+ private :can_perform_case_insensitive_comparison_for?
287
288
 
288
289
  def configure_connection
289
290
  # Here goes initial settings per connection
@@ -340,7 +341,7 @@ module ActiveRecord
340
341
 
341
342
  schemas_and_tables.map do |schema_table|
342
343
  schema, table = schema_table
343
- "#{quote_name_part(schema)}.#{quote_name_part(table)}"
344
+ "#{self.class.mssql_quote_name_part(schema)}.#{self.class.mssql_quote_name_part(table)}"
344
345
  end
345
346
  end
346
347
 
@@ -481,6 +482,8 @@ module ActiveRecord
481
482
  ConnectionNotEstablished.new(exception)
482
483
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
483
484
  RecordNotUnique.new(message, sql: sql, binds: binds)
485
+ when /Violation of PRIMARY KEY constraint .* Cannot insert duplicate key in object .* The duplicate key value is/i
486
+ RecordNotUnique.new(message, sql: sql, binds: binds)
484
487
  when /Lock request time out period exceeded/i
485
488
  LockTimeout.new(message, sql: sql, binds: binds)
486
489
  when /The .* statement conflicted with the FOREIGN KEY constraint/
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArJdbc
4
+ module MSSQLConfig
5
+ def build_connection_config(config)
6
+ config = config.deep_dup
7
+
8
+ load_jdbc_driver
9
+
10
+ config[:driver] ||= database_driver_name
11
+
12
+ config[:host] ||= "localhost"
13
+ config[:connection_alive_sql] ||= "SELECT 1"
14
+ config[:lock_timeout] ||= 5000
15
+
16
+ config[:url] ||= build_connection_url(config)
17
+
18
+ config
19
+ end
20
+
21
+ private
22
+
23
+ def load_jdbc_driver
24
+ require "jdbc/mssql"
25
+
26
+ ::Jdbc::Mssql.load_driver if defined?(::Jdbc::Mssql.load_driver)
27
+ rescue LoadError
28
+ # assuming driver.jar is on the class-path
29
+ end
30
+
31
+ def database_driver_name
32
+ "com.microsoft.sqlserver.jdbc.SQLServerDriver"
33
+ end
34
+
35
+ def build_connection_url(config)
36
+ url = "".dup
37
+ url << "jdbc:sqlserver://#{config[:host]}"
38
+ url << ( config[:port] ? ":#{config[:port]};" : ';' )
39
+ url << "databaseName=#{config[:database]};" if config[:database]
40
+ url << "instanceName=#{config[:instance]};" if config[:instance]
41
+ url << "sendTimeAsDatetime=#{config[:send_time_as_datetime] || false};"
42
+ url << "loginTimeout=#{config[:login_timeout].to_i};" if config[:login_timeout]
43
+ url << "lockTimeout=#{config[:lock_timeout].to_i};"
44
+ url << "encrypt=#{config[:encrypt]};" if config.key?(:encrypt)
45
+ url << "trustServerCertificate=#{config[:trust_server_certificate]};" if config.key?(:trust_server_certificate)
46
+ app = config[:application_name] || config[:appname] || config[:application]
47
+ url << "applicationName=#{app};" if app
48
+ isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
49
+ url << "integratedSecurity=#{isc};" unless isc.nil?
50
+ url
51
+ end
52
+ end
53
+ end
@@ -9,17 +9,26 @@ module ActiveRecord
9
9
  def initialize(name, raw_default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
10
10
  @table_name = table_name
11
11
 
12
- default = extract_default(raw_default)
12
+ default_val, default_fun = extract_default(raw_default)
13
13
 
14
- super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
14
+ super(name, default_val, sql_type_metadata, null, default_fun, collation: collation, comment: comment)
15
15
  end
16
16
 
17
17
  def extract_default(value)
18
- # return nil if default does not match the patterns to avoid
19
- # any unexpected errors.
20
- return unless value =~ /^\(N?'(.*)'\)$/m || value =~ /^\(\(?(.*?)\)?\)$/
21
-
22
- unquote_string(Regexp.last_match[1])
18
+ return [nil, nil] unless value
19
+
20
+ case value
21
+ when /\A\(N?'(.*)'\)\Z/m
22
+ [unquote_string(Regexp.last_match[1]), nil]
23
+ when /\A\(\((.*)\)\)\Z/
24
+ [unquote_string(Regexp.last_match[1]), nil]
25
+ when /\A\((\w+\(\))\)\Z/
26
+ [nil, unquote_string(Regexp.last_match[1])]
27
+ else
28
+ # return nil if default does not match the patterns to avoid
29
+ # any unexpected errors.
30
+ [nil, nil]
31
+ end
23
32
  end
24
33
 
25
34
  def unquote_string(string)
@@ -31,6 +31,8 @@ module ActiveRecord
31
31
 
32
32
  def write_query?(sql) # :nodoc:
33
33
  !READ_QUERY.match?(sql)
34
+ rescue ArgumentError # Invalid encoding
35
+ !READ_QUERY.match?(sql.b)
34
36
  end
35
37
 
36
38
  # Internal method to test different isolation levels supported by this
@@ -90,7 +92,7 @@ module ActiveRecord
90
92
  end
91
93
  end
92
94
 
93
- def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false)
95
+ def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false)
94
96
  sql = transform_query(sql)
95
97
 
96
98
  check_if_write_query(sql)
@@ -176,6 +178,9 @@ module ActiveRecord
176
178
  log(sql, name, binds) do
177
179
  with_raw_connection do |conn|
178
180
  result = conditional_indentity_insert(sql) do
181
+ # DEPRECATION WARNING: to_time will always preserve the timezone offset of the receiver in Rails 8.0.
182
+ # To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`.
183
+ # (called from block in execute_insert_pk
179
184
  conn.execute_insert_pk(sql, binds, pk)
180
185
  end
181
186
  verified!
@@ -244,7 +249,7 @@ module ActiveRecord
244
249
  end
245
250
 
246
251
  def raw_jdbc_connection
247
- @raw_connection
252
+ any_raw_connection
248
253
  end
249
254
 
250
255
  # It seems the truncate_tables is mostly used for testing
@@ -4,6 +4,62 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MSSQL
6
6
  module Quoting
7
+ extend ActiveSupport::Concern
8
+
9
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
+
12
+ module ClassMethods # :nodoc:
13
+ def column_name_matcher
14
+ /
15
+ \A
16
+ (
17
+ (?:
18
+ # \[table_name\].\[column_name\] | function(one or no argument)
19
+ ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
20
+ )
21
+ (?:\s+AS\s+(?:\w+|\[\w+\]))?
22
+ )
23
+ (?:\s*,\s*\g<1>)*
24
+ \z
25
+ /ix
26
+ end
27
+
28
+ def column_name_with_order_matcher
29
+ /
30
+ \A
31
+ (
32
+ (?:
33
+ # \[table_name\].\[column_name\] | function(one or no argument)
34
+ ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
35
+ )
36
+ (?:\s+ASC|\s+DESC)?
37
+ )
38
+ (?:\s*,\s*\g<1>)*
39
+ \z
40
+ /ix
41
+ end
42
+
43
+ def quote_column_name(name)
44
+ QUOTED_COLUMN_NAMES[name] ||= mssql_quote_column_name(name)
45
+ end
46
+
47
+ def quote_table_name(name)
48
+ QUOTED_TABLE_NAMES[name] ||= mssql_quote_column_name(name)
49
+ end
50
+
51
+ def mssql_quote_column_name(name)
52
+ name = name.to_s.split(".")
53
+ name.map! { |n| mssql_quote_name_part(n) } # "[#{name}]"
54
+ name.join(".")
55
+ end
56
+
57
+ # Implements the quoting style for SQL Server
58
+ def mssql_quote_name_part(part)
59
+ part =~ /^\[.*\]$/ ? part : "[#{part.gsub(']', ']]')}]"
60
+ end
61
+ end
62
+
7
63
  QUOTED_TRUE = '1'
8
64
  QUOTED_FALSE = '0'
9
65
 
@@ -88,42 +144,6 @@ module ActiveRecord
88
144
  # @see #quote in old adapter
89
145
  BLOB_VALUE_MARKER = "''"
90
146
 
91
- def column_name_matcher
92
- COLUMN_NAME
93
- end
94
-
95
- def column_name_with_order_matcher
96
- COLUMN_NAME_WITH_ORDER
97
- end
98
-
99
- COLUMN_NAME = /
100
- \A
101
- (
102
- (?:
103
- # \[table_name\].\[column_name\] | function(one or no argument)
104
- ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
105
- )
106
- (?:\s+AS\s+(?:\w+|\[\w+\]))?
107
- )
108
- (?:\s*,\s*\g<1>)*
109
- \z
110
- /ix
111
-
112
- COLUMN_NAME_WITH_ORDER = /
113
- \A
114
- (
115
- (?:
116
- # \[table_name\].\[column_name\] | function(one or no argument)
117
- ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
118
- )
119
- (?:\s+ASC|\s+DESC)?
120
- )
121
- (?:\s*,\s*\g<1>)*
122
- \z
123
- /ix
124
-
125
- private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
126
-
127
147
  private
128
148
 
129
149
  def time_with_db_timezone(value)
@@ -38,6 +38,22 @@ module ActiveRecord
38
38
  end
39
39
  end
40
40
 
41
+ def visit_CreateIndexDefinition(o)
42
+ index = o.index
43
+
44
+ sql = []
45
+ sql << "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '#{o.index.name}')" if o.if_not_exists
46
+ sql << "CREATE"
47
+ sql << "UNIQUE" if index.unique
48
+ sql << index.type.upcase if index.type
49
+ sql << "INDEX"
50
+ sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
51
+ sql << "(#{quoted_columns(index)})"
52
+ sql << "WHERE #{index.where}" if index.where
53
+
54
+ sql.join(" ")
55
+ end
56
+
41
57
  def add_column_options!(sql, options)
42
58
  sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
43
59
 
@@ -80,8 +80,50 @@ module ActiveRecord
80
80
  valid_raw_connection.primary_keys(table_name)
81
81
  end
82
82
 
83
+ def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
84
+ td = create_table_definition(table_name)
85
+ cd = td.new_column_definition(column_name, type, **options)
86
+ ChangeColumnDefinition.new(cd, column_name)
87
+ end
88
+
89
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
90
+ column = column_for(table_name, column_name)
91
+ return unless column
92
+
93
+ default = extract_new_default_value(default_or_changes)
94
+ ChangeColumnDefaultDefinition.new(column, default)
95
+ end
96
+
83
97
  def foreign_keys(table_name)
84
- valid_raw_connection.foreign_keys(table_name)
98
+ # valid_raw_connection.foreign_keys(table_name)
99
+ fk_info = execute_procedure(:sp_fkeys, nil, nil, nil, table_name, nil)
100
+
101
+ grouped_fk = fk_info.group_by { |row| row["FK_NAME"] }.values.each { |group| group.sort_by! { |row| row["KEY_SEQ"] } }
102
+ grouped_fk.map do |group|
103
+ row = group.first
104
+ options = {
105
+ name: row["FK_NAME"],
106
+ on_update: extract_foreign_key_action("update", row["FK_NAME"]),
107
+ on_delete: extract_foreign_key_action("delete", row["FK_NAME"])
108
+ }
109
+
110
+ if group.one?
111
+ options[:column] = row["FKCOLUMN_NAME"]
112
+ options[:primary_key] = row["PKCOLUMN_NAME"]
113
+ else
114
+ options[:column] = group.map { |row| row["FKCOLUMN_NAME"] }
115
+ options[:primary_key] = group.map { |row| row["PKCOLUMN_NAME"] }
116
+ end
117
+
118
+ ForeignKeyDefinition.new(table_name, row["PKTABLE_NAME"], options)
119
+ end
120
+ end
121
+
122
+ def extract_foreign_key_action(action, fk_name)
123
+ case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'")
124
+ when "CASCADE" then :cascade
125
+ when "SET_NULL" then :nullify
126
+ end
85
127
  end
86
128
 
87
129
  def charset
@@ -182,20 +224,8 @@ module ActiveRecord
182
224
  rename_table_indexes(table_name, new_name)
183
225
  end
184
226
 
185
- # This is the same as the abstract method
186
- def quote_table_name(name)
187
- quote_column_name(name)
188
- end
189
-
190
- # This overrides the abstract method to be specific to SQL Server.
191
- def quote_column_name(name)
192
- name = name.to_s.split('.')
193
- name.map! { |n| quote_name_part(n) } # "[#{name}]"
194
- name.join('.')
195
- end
196
-
197
227
  def quote_database_name(name)
198
- quote_name_part(name.to_s)
228
+ self.class.mssql_quote_name_part(name.to_s)
199
229
  end
200
230
 
201
231
  # @private these cannot specify a limit
@@ -439,11 +469,6 @@ module ActiveRecord
439
469
  result
440
470
  end
441
471
 
442
- # Implements the quoting style for SQL Server
443
- def quote_name_part(part)
444
- part =~ /^\[.*\]$/ ? part : "[#{part.gsub(']', ']]')}]"
445
- end
446
-
447
472
  def remove_check_constraints(table_name, column_name)
448
473
  constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA'
449
474
  constraints.each do |constraint|
data/lib/arjdbc/mssql.rb CHANGED
@@ -6,4 +6,4 @@ module ArJdbc
6
6
  MsSQL = MSSQL # compatibility with 1.2
7
7
  end
8
8
 
9
- ArJdbc.warn_unsupported_adapter 'mssql', [7, 1] # warns on AR >= 4.2
9
+ ArJdbc.warn_unsupported_adapter "mssql", [7, 2] # warns on AR >= 4.2
@@ -11,6 +11,8 @@ require 'arjdbc/abstract/database_statements'
11
11
  require 'arjdbc/abstract/statement_cache'
12
12
  require 'arjdbc/abstract/transaction_support'
13
13
 
14
+ require "arjdbc/mysql/adapter_hash_config"
15
+
14
16
  require "arjdbc/abstract/relation_query_attribute_monkey_patch"
15
17
 
16
18
  module ActiveRecord
@@ -34,6 +36,7 @@ module ActiveRecord
34
36
  include ArJdbc::Abstract::TransactionSupport
35
37
 
36
38
  include ArJdbc::MySQL
39
+ include ArJdbc::MysqlConfig
37
40
 
38
41
  class << self
39
42
  def jdbc_connection_class
@@ -68,6 +71,9 @@ module ActiveRecord
68
71
 
69
72
  @config[:flags] ||= 0
70
73
 
74
+ # assign arjdbc extra connection params
75
+ conn_params = build_connection_config(@config.compact)
76
+
71
77
  # JDBC mysql appears to use found rows by default: https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-connection.html
72
78
  # if @config[:flags].kind_of? Array
73
79
  # @config[:flags].push "FOUND_ROWS"
@@ -75,16 +81,7 @@ module ActiveRecord
75
81
  # @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
76
82
  # end
77
83
 
78
- @connection_parameters ||= @config
79
- end
80
-
81
- def self.database_exists?(config)
82
- conn = ActiveRecord::Base.mysql2_connection(config)
83
- conn && conn.really_valid?
84
- rescue ActiveRecord::NoDatabaseError
85
- false
86
- ensure
87
- conn.disconnect! if conn
84
+ @connection_parameters = conn_params
88
85
  end
89
86
 
90
87
  def supports_json?
@@ -244,7 +241,7 @@ module ActiveRecord
244
241
 
245
242
  # e.g. "5.7.20-0ubuntu0.16.04.1"
246
243
  def full_version
247
- schema_cache.database_version.full_version_string
244
+ database_version.full_version_string
248
245
  end
249
246
 
250
247
  def get_full_version