Skip to content

Commit 8849ef0

Browse files
committed
Support table names containing spaces (#1206)
1 parent 0b61b94 commit 8849ef0

File tree

5 files changed

+76
-7
lines changed

5 files changed

+76
-7
lines changed

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

+12-6
Original file line numberDiff line numberDiff line change
@@ -673,12 +673,18 @@ def get_table_name(sql)
673673

674674
# Parses the raw table name that is used in the SQL. Table name could include database/schema/etc.
675675
def get_raw_table_name(sql)
676-
case sql
677-
when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
678-
Regexp.last_match[3] || Regexp.last_match[4]
679-
when /FROM\s+([^\(\s]+)\s*/i
680-
Regexp.last_match[1]
681-
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
682688
end
683689

684690
def default_constraint_name(table_name, column_name)

test/cases/adapter_test_sqlserver.rb

+8
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,14 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
561561
end
562562
end
563563

564+
describe 'table names contains spaces' do
565+
it 'records can be created successfully' do
566+
assert_difference("TableWithSpaces.count", 1) do
567+
TableWithSpaces.create!(name: 'Bob')
568+
end
569+
end
570+
end
571+
564572
describe "exec_insert" do
565573
it 'values clause should be case-insensitive' do
566574
assert_difference("Post.count", 4) do

test/cases/schema_test_sqlserver.rb

+47-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
3939
assert_equal 1, columns.select { |c| c.is_identity? }.size
4040
end
4141

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
4343
columns = connection.columns("test.sst_schema_columns")
4444

4545
assert_equal 255, columns.find { |c| c.name == "name" }.limit
@@ -48,4 +48,50 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
4848
assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit
4949
end
5050
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
5197
end
Original file line numberDiff line numberDiff line change
@@ -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

test/schema/sqlserver_specific_schema.rb

+4
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@
151151
SELECT GETUTCDATE() utcdate
152152
SQL
153153

154+
create_table 'A Table With Spaces', force: true do |t|
155+
t.string :name
156+
end
157+
154158
# Constraints
155159

156160
create_table(:sst_has_fks, force: true) do |t|

0 commit comments

Comments
 (0)