activerecord-sqlserver-adapter 7.1.0.rc1 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42ca12c2b62c162e798e40d6ca3ec7861e736835d8d91f132eef16240dcfeb02
4
- data.tar.gz: ddf249b287e32a11a03ea4b2a9bfe85dbbded619d83165559f108892d90da658
3
+ metadata.gz: 58c19846ce74967bacd12d1accd41c82b31f2fb04829ee8a19ea529f73dd97a7
4
+ data.tar.gz: 81d8ea6ee564704c1bf0178c3e28de18979892a0b18881eb3cee2f5d96052101
5
5
  SHA512:
6
- metadata.gz: dde118ab5b86cd3b154739ce66933e8d4f3380503e6626796941c93cd08775571ad1f6b1630d7a5882b8dc97e0cd5475a7043fd8ac792af723e7c85720ff2271
7
- data.tar.gz: 80efec8b5e389f05c57a1a2bab01cae17addbda59ec772a602f9aa1e4212508301a954b32d795d0ec6898c161226d36664a169f834c3f34e3ce1fd1c34a56d0e
6
+ metadata.gz: 9369b4c86d3ea8ef36eeb47a19e5e2bc5a31024ead3aa1e1d535346a486526e0e9e196d95e8f2667a57d1532f8daa5f48b7909e657eb2c699450480e23974e85
7
+ data.tar.gz: c10c097a4ebd447e7dd25b6f6235023773a15bbf8c3650c7cc3e73cee27b4a37c82a85b493b807b71bf5fd971258fd40300e3696b3a92f4a8e585f55e0562cf0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## v7.1.0
2
+
3
+ #### Added
4
+
5
+ - [#1141](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1141) Added support for check constraints.
6
+
7
+ ## v7.1.0.rc2
8
+
9
+ #### Added
10
+
11
+ - [#1136](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1136) Prevent marking broken connections as verified
12
+ - [#1138](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1138) Cache quoted names
13
+
1
14
  ## v7.1.0.rc1
2
15
 
3
16
  #### Added
data/README.md CHANGED
@@ -11,16 +11,16 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher.
11
11
 
12
12
  Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord.
13
13
 
14
- | Adapter Version | Rails Version | Support | Branch |
15
- |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------|
16
- | `7.1.0.rc1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
17
- | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) |
18
- | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
19
- | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
20
- | `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) |
21
- | `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) |
22
- | `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) |
23
- | `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) |
14
+ | Adapter Version | Rails Version | Support | Branch |
15
+ |-----------------|---------------|---------|--------------------------------------------------------------------------------------------------|
16
+ | `7.1.0` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
17
+ | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) |
18
+ | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
19
+ | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
20
+ | `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) |
21
+ | `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) |
22
+ | `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) |
23
+ | `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) |
24
24
 
25
25
  For older versions, please check their stable branches.
26
26
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.1.0.rc1
1
+ 7.1.0
@@ -23,6 +23,7 @@ module ActiveRecord
23
23
  else
24
24
  internal_raw_execute(sql, conn, perform_do: true)
25
25
  end
26
+ verified!
26
27
  end
27
28
  end
28
29
 
@@ -50,6 +51,7 @@ module ActiveRecord
50
51
  else
51
52
  result = internal_exec_sql_query(sql, conn)
52
53
  end
54
+ verified!
53
55
  end
54
56
  end
55
57
 
@@ -175,6 +177,7 @@ module ActiveRecord
175
177
  log(sql, "Execute Procedure") do
176
178
  with_raw_connection do |conn|
177
179
  result = internal_raw_execute(sql, conn)
180
+ verified!
178
181
  options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc }
179
182
 
180
183
  result.each(options) do |row|
@@ -4,18 +4,18 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module Quoting
7
- QUOTED_TRUE = "1".freeze
8
- QUOTED_FALSE = "0".freeze
9
- QUOTED_STRING_PREFIX = "N".freeze
7
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
10
9
 
11
10
  def fetch_type_metadata(sql_type, sqlserver_options = {})
12
11
  cast_type = lookup_cast_type(sql_type)
12
+
13
13
  simple_type = SqlTypeMetadata.new(
14
- sql_type: sql_type,
15
- type: cast_type.type,
16
- limit: cast_type.limit,
14
+ sql_type: sql_type,
15
+ type: cast_type.type,
16
+ limit: cast_type.limit,
17
17
  precision: cast_type.precision,
18
- scale: cast_type.scale
18
+ scale: cast_type.scale
19
19
  )
20
20
 
21
21
  SQLServer::TypeMetadata.new(simple_type, **sqlserver_options)
@@ -34,7 +34,11 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  def quote_column_name(name)
37
- SQLServer::Utils.extract_identifiers(name).quoted
37
+ QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
38
+ end
39
+
40
+ def quote_table_name(name)
41
+ QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
38
42
  end
39
43
 
40
44
  def quote_default_expression(value, column)
@@ -47,7 +51,7 @@ module ActiveRecord
47
51
  end
48
52
 
49
53
  def quoted_true
50
- QUOTED_TRUE
54
+ '1'
51
55
  end
52
56
 
53
57
  def unquoted_true
@@ -55,7 +59,7 @@ module ActiveRecord
55
59
  end
56
60
 
57
61
  def quoted_false
58
- QUOTED_FALSE
62
+ '0'
59
63
  end
60
64
 
61
65
  def unquoted_false
@@ -117,7 +121,7 @@ module ActiveRecord
117
121
  when ActiveRecord::Type::SQLServer::Data
118
122
  value.quoted
119
123
  when String, ActiveSupport::Multibyte::Chars
120
- "#{QUOTED_STRING_PREFIX}#{super}"
124
+ "N#{super}"
121
125
  else
122
126
  super
123
127
  end
@@ -267,6 +267,29 @@ module ActiveRecord
267
267
  end
268
268
  end
269
269
 
270
+ def check_constraints(table_name)
271
+ sql = <<~SQL
272
+ select chk.name AS 'name',
273
+ chk.definition AS 'expression'
274
+ from sys.check_constraints chk
275
+ inner join sys.tables st on chk.parent_object_id = st.object_id
276
+ where
277
+ st.name = '#{table_name}'
278
+ SQL
279
+
280
+ chk_info = internal_exec_query(sql, "SCHEMA")
281
+
282
+ chk_info.map do |row|
283
+ options = {
284
+ name: row["name"]
285
+ }
286
+ expression = row["expression"]
287
+ expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
288
+
289
+ CheckConstraintDefinition.new(table_name, expression, options)
290
+ end
291
+ end
292
+
270
293
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
271
294
  type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
272
295
  limit = nil unless type_limitable
@@ -6,13 +6,9 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module SQLServer
8
8
  module Utils
9
- QUOTED_STRING_PREFIX = "N"
10
-
11
9
  # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
12
10
  # Inspired from Rails PostgreSQL::Name adapter object in their own Utils.
13
- #
14
11
  class Name
15
- SEPARATOR = "."
16
12
  UNQUOTED_SCANNER = /\]?\./
17
13
  QUOTED_SCANNER = /\A\[.*?\]\./
18
14
  QUOTED_CHECKER = /\A\[/
@@ -42,7 +38,7 @@ module ActiveRecord
42
38
  end
43
39
 
44
40
  def fully_qualified_database_quoted
45
- [server_quoted, database_quoted].compact.join(SEPARATOR)
41
+ [server_quoted, database_quoted].compact.join('.')
46
42
  end
47
43
 
48
44
  def fully_qualified?
@@ -69,7 +65,7 @@ module ActiveRecord
69
65
  end
70
66
 
71
67
  def quoted
72
- parts.map { |p| quote(p) if p }.join SEPARATOR
68
+ parts.map { |p| quote(p) if p }.join('.')
73
69
  end
74
70
 
75
71
  def quoted_raw
@@ -132,7 +128,7 @@ module ActiveRecord
132
128
  extend self
133
129
 
134
130
  def quote_string(s)
135
- s.to_s.gsub /\'/, "''"
131
+ s.to_s.gsub(/\'/, "''")
136
132
  end
137
133
 
138
134
  def quote_string_single(s)
@@ -140,7 +136,7 @@ module ActiveRecord
140
136
  end
141
137
 
142
138
  def quote_string_single_national(s)
143
- "#{QUOTED_STRING_PREFIX}'#{quote_string(s)}'"
139
+ "N'#{quote_string(s)}'"
144
140
  end
145
141
 
146
142
  def quoted_raw(name)
@@ -171,6 +171,10 @@ module ActiveRecord
171
171
  true
172
172
  end
173
173
 
174
+ def supports_check_constraints?
175
+ true
176
+ end
177
+
174
178
  def supports_json?
175
179
  version_year >= 2016
176
180
  end
@@ -53,7 +53,7 @@ module ActiveRecord
53
53
  end
54
54
 
55
55
  def clear_active_connections!
56
- ActiveRecord::Base.connection_handler.clear_active_connections!
56
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
57
57
  end
58
58
 
59
59
  def structure_dump(filename, extra_flags)
@@ -64,6 +64,38 @@ module Arel
64
64
  super
65
65
  end
66
66
 
67
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
68
+ collector.preparable = false
69
+
70
+ visit o.left, collector
71
+
72
+ if o.type == :in
73
+ collector << " IN ("
74
+ else
75
+ collector << " NOT IN ("
76
+ end
77
+
78
+ values = o.casted_values
79
+
80
+ # Monkey-patch start.
81
+ column_name = o.attribute.name
82
+ column_type = o.attribute.relation.type_for_attribute(column_name)
83
+ column_type = column_type.cast_type if column_type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) # Use cast_type on encrypted attributes. Don't encrypt them again
84
+
85
+ if values.empty?
86
+ collector << @connection.quote(nil)
87
+ elsif @connection.prepared_statements && !column_type.serialized?
88
+ # Add query attribute bindings rather than just values.
89
+ attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) }
90
+ collector.add_binds(attrs, &bind_block)
91
+ else
92
+ collector.add_binds(values, o.proc_for_binds, &bind_block)
93
+ end
94
+ # Monkey-patch end.
95
+
96
+ collector << ")"
97
+ end
98
+
67
99
  def visit_Arel_Nodes_SelectStatement(o, collector)
68
100
  @select_statement = o
69
101
  distinct_One_As_One_Is_So_Not_Fetch o
@@ -1580,6 +1580,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
1580
1580
 
1581
1581
  # Tests are not about a specific adapter.
1582
1582
  coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config
1583
+
1584
+ # SQL Server formats the check constraint expression differently.
1585
+ coerce_tests! :test_schema_dumps_check_constraints
1586
+ def test_schema_dumps_check_constraints_coerced
1587
+ constraint_definition = dump_table_schema("products").split(/\n/).grep(/t.check_constraint.*products_price_check/).first.strip
1588
+ assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition
1589
+ end
1583
1590
  end
1584
1591
 
1585
1592
  class SchemaDumperDefaultsTest < ActiveRecord::TestCase
@@ -2585,3 +2592,54 @@ module ActiveRecord
2585
2592
  end
2586
2593
  end
2587
2594
  end
2595
+
2596
+ module ActiveRecord
2597
+ class Migration
2598
+ class CheckConstraintTest < ActiveRecord::TestCase
2599
+ # SQL Server formats the check constraint expression differently.
2600
+ coerce_tests! :test_check_constraints
2601
+ def test_check_constraints_coerced
2602
+ check_constraints = @connection.check_constraints("products")
2603
+ assert_equal 1, check_constraints.size
2604
+
2605
+ constraint = check_constraints.first
2606
+ assert_equal "products", constraint.table_name
2607
+ assert_equal "products_price_check", constraint.name
2608
+ assert_equal "[price]>[discounted_price]", constraint.expression
2609
+ end
2610
+
2611
+ # SQL Server formats the check constraint expression differently.
2612
+ coerce_tests! :test_add_check_constraint
2613
+ def test_add_check_constraint_coerced
2614
+ @connection.add_check_constraint :trades, "quantity > 0"
2615
+
2616
+ check_constraints = @connection.check_constraints("trades")
2617
+ assert_equal 1, check_constraints.size
2618
+
2619
+ constraint = check_constraints.first
2620
+ assert_equal "trades", constraint.table_name
2621
+ assert_equal "chk_rails_2189e9f96c", constraint.name
2622
+ assert_equal "[quantity]>(0)", constraint.expression
2623
+ end
2624
+
2625
+ # SQL Server formats the check constraint expression differently.
2626
+ coerce_tests! :test_remove_check_constraint
2627
+ def test_remove_check_constraint_coerced
2628
+ @connection.add_check_constraint :trades, "price > 0", name: "price_check"
2629
+ @connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check"
2630
+
2631
+ assert_equal 2, @connection.check_constraints("trades").size
2632
+ @connection.remove_check_constraint :trades, name: "quantity_check"
2633
+ assert_equal 1, @connection.check_constraints("trades").size
2634
+
2635
+ constraint = @connection.check_constraints("trades").first
2636
+ assert_equal "trades", constraint.table_name
2637
+ assert_equal "price_check", constraint.name
2638
+ assert_equal "[price]>(0)", constraint.expression
2639
+
2640
+ @connection.remove_check_constraint :trades, name: :price_check # name as a symbol
2641
+ assert_empty @connection.check_constraints("trades")
2642
+ end
2643
+ end
2644
+ end
2645
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-sqlserver-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.0.rc1
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2023-11-09 00:00:00.000000000 Z
18
+ date: 2023-11-21 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
@@ -235,8 +235,8 @@ licenses:
235
235
  - MIT
236
236
  metadata:
237
237
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
238
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.1.0.rc1/CHANGELOG.md
239
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.1.0.rc1
238
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.1.0/CHANGELOG.md
239
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.1.0
240
240
  post_install_message:
241
241
  rdoc_options: []
242
242
  require_paths:
@@ -248,9 +248,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
248
248
  version: 2.7.0
249
249
  required_rubygems_version: !ruby/object:Gem::Requirement
250
250
  requirements:
251
- - - ">"
251
+ - - ">="
252
252
  - !ruby/object:Gem::Version
253
- version: 1.3.1
253
+ version: '0'
254
254
  requirements: []
255
255
  rubygems_version: 3.4.7
256
256
  signing_key: