activerecord-sqlserver-adapter 7.1.4 → 7.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e12d59815d11d0cbecea5d206057d6251384ef3a37df229c3c20c92eb7e0312
4
- data.tar.gz: 25644794411a6dd0e709216a5cc444571645f94f2faf07ede21a6680c9682b35
3
+ metadata.gz: eea983d328f36bfb590c26ff592292369634d60b63ff38acc43e28c87e09c7ee
4
+ data.tar.gz: 7e9ab05308f76eb6be6ef5745977f4c0de684c608494d45d9d49d3ac54e1a4cb
5
5
  SHA512:
6
- metadata.gz: ff6fc5d218ed61de3c5b28024f72e42023ee15e710048f797e428d00487dd3be8284fadc8a23922aacb08ae22389aa0eaf7d9e86123d1a9d23ee9f2f222cc54e
7
- data.tar.gz: ee6cd4a68000ec209f06f39c5029b049dd870993652a9bce9125cc118efc829bced1a921631fec7a8353e7bf96790eacdd9718a606fa82dee8ff2e74143b45c5
6
+ metadata.gz: 67f76bc1083712be03b8a8b3eb5a2c48b9d4efc44442b8573604aecaff23ccbd0bd1586529948497776aca6b7c18257ead5d62e239f2b69d92e2ec14e5675192
7
+ data.tar.gz: 845923a58bda81c5f32a81b40a61157b1d36c909352ac258bd848d2e604f95ee94e09878f3c8f475f3d770725c8492b8b58c4c87146c4965e0b29d91b64872fc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## v7.1.5
2
+
3
+ #### Added
4
+
5
+ - [#1201](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1201) Support non-dbo schemas in schema dumper
6
+ - [#1206](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1206) Support table names containing spaces
7
+
1
8
  ## v7.1.4
2
9
 
3
10
  #### Fixed
data/README.md CHANGED
@@ -13,7 +13,8 @@ Interested in older versions? We follow a rational versioning policy that tracks
13
13
 
14
14
  | Adapter Version | Rails Version | Support | Branch |
15
15
  |-----------------|---------------|---------|--------------------------------------------------------------------------------------------------|
16
- | `7.1.4` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
16
+ | Unreleased | `7.2.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) |
17
+ | `7.1.5` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) |
17
18
  | `7.0.7` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) |
18
19
  | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) |
19
20
  | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) |
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.1.4
1
+ 7.1.5
@@ -39,6 +39,17 @@ module ActiveRecord
39
39
  def default_primary_key?(column)
40
40
  super && column.is_identity?
41
41
  end
42
+
43
+ def schemas(stream)
44
+ schema_names = @connection.schema_names
45
+
46
+ if schema_names.any?
47
+ schema_names.sort.each do |name|
48
+ stream.puts " create_schema #{name.inspect}"
49
+ end
50
+ stream.puts
51
+ end
52
+ end
42
53
  end
43
54
  end
44
55
  end
@@ -393,19 +393,37 @@ module ActiveRecord
393
393
  execute "DROP SCHEMA [#{schema_name}]"
394
394
  end
395
395
 
396
+ # Returns an array of schema names.
397
+ def schema_names
398
+ sql = <<~SQL.squish
399
+ SELECT name
400
+ FROM sys.schemas
401
+ WHERE
402
+ name NOT LIKE 'db_%' AND
403
+ name NOT IN ('INFORMATION_SCHEMA', 'sys')
404
+ SQL
405
+
406
+ query_values(sql, "SCHEMA")
407
+ end
408
+
396
409
  private
397
410
 
398
411
  def data_source_sql(name = nil, type: nil)
399
- scope = quoted_scope name, type: type
412
+ scope = quoted_scope(name, type: type)
400
413
 
401
- table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
402
- database = scope[:database].present? ? "#{scope[:database]}." : ""
414
+ table_schema = lowercase_schema_reflection_sql('TABLE_SCHEMA')
415
+ table_name = lowercase_schema_reflection_sql('TABLE_NAME')
416
+ database = scope[:database].present? ? "#{scope[:database]}." : ""
403
417
  table_catalog = scope[:database].present? ? quote(scope[:database]) : "DB_NAME()"
404
418
 
405
- sql = "SELECT #{table_name}"
419
+ sql = "SELECT "
420
+ sql += " CASE"
421
+ sql += " WHEN #{table_schema} = 'dbo' THEN #{table_name}"
422
+ sql += " ELSE CONCAT(#{table_schema}, '.', #{table_name})"
423
+ sql += " END"
406
424
  sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
407
425
  sql += " WHERE TABLE_CATALOG = #{table_catalog}"
408
- sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
426
+ sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" if scope[:schema]
409
427
  sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
410
428
  sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
411
429
  sql += " ORDER BY #{table_name}"
@@ -414,9 +432,10 @@ module ActiveRecord
414
432
 
415
433
  def quoted_scope(name = nil, type: nil)
416
434
  identifier = SQLServer::Utils.extract_identifiers(name)
435
+
417
436
  {}.tap do |scope|
418
437
  scope[:database] = identifier.database if identifier.database
419
- scope[:schema] = identifier.schema || "dbo"
438
+ scope[:schema] = identifier.schema || "dbo" if name.present?
420
439
  scope[:name] = identifier.object if identifier.object
421
440
  scope[:type] = type if type
422
441
  end
@@ -654,12 +673,18 @@ module ActiveRecord
654
673
 
655
674
  # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc.
656
675
  def get_raw_table_name(sql)
657
- case sql
658
- when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
659
- Regexp.last_match[3] || Regexp.last_match[4]
660
- when /FROM\s+([^\(\s]+)\s*/i
661
- Regexp.last_match[1]
662
- end
676
+ s = sql.gsub(/^\s*EXEC sp_executesql N'/i, "")
677
+
678
+ if s.match?(/^\s*INSERT INTO.*/i)
679
+ s.split(/INSERT INTO/i)[1]
680
+ .split(/OUTPUT INSERTED/i)[0]
681
+ .split(/(DEFAULT)?\s+VALUES/i)[0]
682
+ .match(/\s*([^(]*)/i)[0]
683
+ elsif s.match?(/^\s*UPDATE\s+.*/i)
684
+ s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1]
685
+ else
686
+ s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1]
687
+ end.strip
663
688
  end
664
689
 
665
690
  def default_constraint_name(table_name, column_name)
@@ -550,11 +550,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
550
550
 
551
551
  describe 'table is in non-dbo schema' do
552
552
  it "records can be created successfully" do
553
- Alien.create!(name: 'Trisolarans')
553
+ assert_difference("Alien.count", 1) do
554
+ Alien.create!(name: 'Trisolarans')
555
+ end
554
556
  end
555
557
 
556
558
  it 'records can be inserted using SQL' do
557
- Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
559
+ assert_difference("Alien.count", 2) do
560
+ Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
561
+ end
562
+ end
563
+ end
564
+
565
+ describe 'table names contains spaces' do
566
+ it 'records can be created successfully' do
567
+ assert_difference("TableWithSpaces.count", 1) do
568
+ TableWithSpaces.create!(name: 'Bob')
569
+ end
558
570
  end
559
571
  end
560
572
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cases/helper_sqlserver"
4
+ require "stringio"
4
5
 
5
6
  class SchemaDumperTestSQLServer < ActiveRecord::TestCase
6
7
  before { all_tables }
@@ -141,7 +142,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
141
142
  it "honor nonstandard primary keys" do
142
143
  generate_schema_for_table("movies") do |output|
143
144
  match = output.match(%r{create_table "movies"(.*)do})
144
- assert_not_nil(match, "nonstandardpk table not found")
145
+ assert_not_nil(match, "non-standard primary key table not found")
145
146
  assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved"
146
147
  end
147
148
  end
@@ -159,14 +160,32 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
159
160
  _(output.scan('t.integer "unique_field"').length).must_equal(1)
160
161
  end
161
162
 
163
+ it "schemas are dumped and tables names only include non-default schema" do
164
+ stream = StringIO.new
165
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
166
+ generated_schema = stream.string
167
+
168
+ # Only generate non-default schemas. Default schema is 'dbo'.
169
+ assert_not_includes generated_schema, 'create_schema "dbo"'
170
+ assert_includes generated_schema, 'create_schema "test"'
171
+ assert_includes generated_schema, 'create_schema "test2"'
172
+
173
+ # Only non-default schemas should be included in table names. Default schema is 'dbo'.
174
+ assert_includes generated_schema, 'create_table "accounts"'
175
+ assert_includes generated_schema, 'create_table "test.aliens"'
176
+ assert_includes generated_schema, 'create_table "test2.sst_schema_test_mulitple_schema"'
177
+ end
178
+
162
179
  private
163
180
 
164
181
  def generate_schema_for_table(*table_names)
165
- require "stringio"
182
+ previous_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
183
+ ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
166
184
 
167
185
  stream = StringIO.new
168
186
  ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
169
187
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
188
+
170
189
  @generated_schema = stream.string
171
190
  yield @generated_schema if block_given?
172
191
  @schema_lines = Hash.new
@@ -177,6 +196,8 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
177
196
  @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line)
178
197
  end
179
198
  @generated_schema
199
+ ensure
200
+ ActiveRecord::SchemaDumper.ignore_tables = previous_ignore_tables
180
201
  end
181
202
 
182
203
  def line(column_name)
@@ -39,7 +39,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
39
39
  assert_equal 1, columns.select { |c| c.is_identity? }.size
40
40
  end
41
41
 
42
- it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do
42
+ it "return correct varchar and nvarchar column limit length when table is in non-dbo schema" do
43
43
  columns = connection.columns("test.sst_schema_columns")
44
44
 
45
45
  assert_equal 255, columns.find { |c| c.name == "name" }.limit
@@ -48,4 +48,50 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
48
48
  assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit
49
49
  end
50
50
  end
51
+
52
+ describe "parsing table name from raw SQL" do
53
+ describe 'SELECT statements' do
54
+ it do
55
+ assert_equal "[sst_schema_columns]", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM [sst_schema_columns]")
56
+ end
57
+
58
+ it do
59
+ assert_equal "sst_schema_columns", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM sst_schema_columns")
60
+ end
61
+
62
+ it do
63
+ assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES]")
64
+ end
65
+
66
+ it do
67
+ assert_equal "[WITH - SPACES$DOLLAR]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES$DOLLAR]")
68
+ end
69
+ end
70
+
71
+ describe 'INSERT statements' do
72
+ it do
73
+ assert_equal "[dashboards]", connection.send(:get_raw_table_name, "INSERT INTO [dashboards] DEFAULT VALUES; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
74
+ end
75
+
76
+ it do
77
+ assert_equal "lock_without_defaults", connection.send(:get_raw_table_name, "INSERT INTO lock_without_defaults(title) VALUES('title1')")
78
+ end
79
+
80
+ it do
81
+ assert_equal "json_data_type", connection.send(:get_raw_table_name, "insert into json_data_type (payload) VALUES ('null')")
82
+ end
83
+
84
+ it do
85
+ assert_equal "[auto_increments]", connection.send(:get_raw_table_name, "INSERT INTO [auto_increments] OUTPUT INSERTED.[id] DEFAULT VALUES")
86
+ end
87
+
88
+ it do
89
+ assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [WITH - SPACES] ([external_id]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 bigint', @0 = 10")
90
+ end
91
+
92
+ it do
93
+ assert_equal "[test].[aliens]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 varchar(255)', @0 = 'Trisolarans'")
94
+ end
95
+ end
96
+ end
51
97
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TableWithSpaces < ActiveRecord::Base
4
+ self.table_name = "A Table With Spaces"
5
+ end
@@ -151,6 +151,10 @@ ActiveRecord::Schema.define do
151
151
  SELECT GETUTCDATE() utcdate
152
152
  SQL
153
153
 
154
+ create_table 'A Table With Spaces', force: true do |t|
155
+ t.string :name
156
+ end
157
+
154
158
  # Constraints
155
159
 
156
160
  create_table(:sst_has_fks, force: true) do |t|
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.4
4
+ version: 7.1.5
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: 2024-07-04 00:00:00.000000000 Z
18
+ date: 2024-07-25 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
@@ -212,6 +212,7 @@ files:
212
212
  - test/models/sqlserver/string_default.rb
213
213
  - test/models/sqlserver/string_defaults_big_view.rb
214
214
  - test/models/sqlserver/string_defaults_view.rb
215
+ - test/models/sqlserver/table_with_spaces.rb
215
216
  - test/models/sqlserver/tinyint_pk.rb
216
217
  - test/models/sqlserver/trigger.rb
217
218
  - test/models/sqlserver/trigger_history.rb
@@ -239,8 +240,8 @@ licenses:
239
240
  - MIT
240
241
  metadata:
241
242
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
242
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.1.4/CHANGELOG.md
243
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.1.4
243
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v7.1.5/CHANGELOG.md
244
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v7.1.5
244
245
  post_install_message:
245
246
  rdoc_options: []
246
247
  require_paths:
@@ -329,6 +330,7 @@ test_files:
329
330
  - test/models/sqlserver/string_default.rb
330
331
  - test/models/sqlserver/string_defaults_big_view.rb
331
332
  - test/models/sqlserver/string_defaults_view.rb
333
+ - test/models/sqlserver/table_with_spaces.rb
332
334
  - test/models/sqlserver/tinyint_pk.rb
333
335
  - test/models/sqlserver/trigger.rb
334
336
  - test/models/sqlserver/trigger_history.rb