Skip to content

Support table names containing spaces #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

#### Added

- [#1201](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1201) Support non-dbo schemas in schema dumper.
- [#1201](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1201) Support non-dbo schemas in schema dumper
- [#1206](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1206) Support table names containing spaces

## v7.1.4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,12 +673,18 @@ def get_table_name(sql)

# Parses the raw table name that is used in the SQL. Table name could include database/schema/etc.
def get_raw_table_name(sql)
case sql
when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
Regexp.last_match[3] || Regexp.last_match[4]
when /FROM\s+([^\(\s]+)\s*/i
Regexp.last_match[1]
end
s = sql.gsub(/^\s*EXEC sp_executesql N'/i, "")

if s.match?(/^\s*INSERT INTO.*/i)
s.split(/INSERT INTO/i)[1]
.split(/OUTPUT INSERTED/i)[0]
.split(/(DEFAULT)?\s+VALUES/i)[0]
.match(/\s*([^(]*)/i)[0]
elsif s.match?(/^\s*UPDATE\s+.*/i)
s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1]
else
s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1]
end.strip
end

def default_constraint_name(table_name, column_name)
Expand Down
16 changes: 14 additions & 2 deletions test/cases/adapter_test_sqlserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,23 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes

describe 'table is in non-dbo schema' do
it "records can be created successfully" do
Alien.create!(name: 'Trisolarans')
assert_difference("Alien.count", 1) do
Alien.create!(name: 'Trisolarans')
end
end

it 'records can be inserted using SQL' do
Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
assert_difference("Alien.count", 2) do
Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
end
end
end

describe 'table names contains spaces' do
it 'records can be created successfully' do
assert_difference("TableWithSpaces.count", 1) do
TableWithSpaces.create!(name: 'Bob')
end
end
end

Expand Down
48 changes: 47 additions & 1 deletion test/cases/schema_test_sqlserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
assert_equal 1, columns.select { |c| c.is_identity? }.size
end

it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do
it "return correct varchar and nvarchar column limit length when table is in non-dbo schema" do
columns = connection.columns("test.sst_schema_columns")

assert_equal 255, columns.find { |c| c.name == "name" }.limit
Expand All @@ -48,4 +48,50 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit
end
end

describe "parsing table name from raw SQL" do
describe 'SELECT statements' do
it do
assert_equal "[sst_schema_columns]", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM [sst_schema_columns]")
end

it do
assert_equal "sst_schema_columns", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM sst_schema_columns")
end

it do
assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES]")
end

it do
assert_equal "[WITH - SPACES$DOLLAR]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES$DOLLAR]")
end
end

describe 'INSERT statements' do
it do
assert_equal "[dashboards]", connection.send(:get_raw_table_name, "INSERT INTO [dashboards] DEFAULT VALUES; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
end

it do
assert_equal "lock_without_defaults", connection.send(:get_raw_table_name, "INSERT INTO lock_without_defaults(title) VALUES('title1')")
end

it do
assert_equal "json_data_type", connection.send(:get_raw_table_name, "insert into json_data_type (payload) VALUES ('null')")
end

it do
assert_equal "[auto_increments]", connection.send(:get_raw_table_name, "INSERT INTO [auto_increments] OUTPUT INSERTED.[id] DEFAULT VALUES")
end

it do
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")
end

it do
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'")
end
end
end
end
5 changes: 5 additions & 0 deletions test/models/sqlserver/table_with_spaces.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class TableWithSpaces < ActiveRecord::Base
self.table_name = "A Table With Spaces"
end
4 changes: 4 additions & 0 deletions test/schema/sqlserver_specific_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@
SELECT GETUTCDATE() utcdate
SQL

create_table 'A Table With Spaces', force: true do |t|
t.string :name
end

# Constraints

create_table(:sst_has_fks, force: true) do |t|
Expand Down
Loading