Skip to content

Commit c98ea79

Browse files
authored
Dump column collation to schema.rb and allow collation changes using column_change (#881)
* add column collation to schema dump. Dump string value * be able to alter column collation * spec showing collation change using 'change_column' * update changelog
1 parent 5f0c423 commit c98ea79

File tree

10 files changed

+90
-1
lines changed

10 files changed

+90
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating
1212
- [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config
1313
- [#885](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix the quoting of ActiveModel attributes
14+
- [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change
1415

1516
#### Changed
1617

lib/active_record/connection_adapters/sqlserver/schema_creation.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ def add_column_options!(sql, options)
3838
if options[:null] == false
3939
sql << " NOT NULL"
4040
end
41+
if options[:collation].present?
42+
sql << " COLLATE #{options[:collation]}"
43+
end
4144
if options[:is_identity] == true
4245
sql << " IDENTITY(1,1)"
4346
end

lib/active_record/connection_adapters/sqlserver/schema_dumper.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ def schema_limit(column)
2727
def schema_collation(column)
2828
return unless column.collation
2929

30-
column.collation if column.collation != @connection.collation
30+
# use inspect to ensure collation is dumped as string. Without this it's dumped as
31+
# a constant ('collation: SQL_Latin1_General_CP1_CI_AS')
32+
collation = column.collation.inspect
33+
# use inspect to ensure string comparison
34+
default_collation = @connection.collation.inspect
35+
36+
collation if collation != default_collation
3137
end
3238

3339
def default_primary_key?(column)

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ def change_column(table_name, column_name, type, options = {})
156156
end
157157
sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
158158
alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
159+
alter_command += " COLLATE #{options[:collation]}" if options[:collation].present?
159160
alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
160161
sql_commands << alter_command
161162
if without_constraints
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
require "cases/helper_sqlserver"
4+
require "migrations/create_clients_and_change_column_collation"
5+
6+
class ChangeColumnCollationTestSqlServer < ActiveRecord::TestCase
7+
before do
8+
@old_verbose = ActiveRecord::Migration.verbose
9+
ActiveRecord::Migration.verbose = false
10+
CreateClientsAndChangeColumnCollation.new.up
11+
end
12+
13+
after do
14+
CreateClientsAndChangeColumnCollation.new.down
15+
ActiveRecord::Migration.verbose = @old_verbose
16+
end
17+
18+
def find_column(table, name)
19+
table.find { |column| column.name == name }
20+
end
21+
22+
let(:clients_table) { connection.columns("clients") }
23+
let(:name_column) { find_column(clients_table, "name") }
24+
let(:code_column) { find_column(clients_table, "code") }
25+
26+
it "change column collation to other than default" do
27+
_(name_column.collation).must_equal "SQL_Latin1_General_CP1_CS_AS"
28+
end
29+
30+
it "change column collation to default" do
31+
_(code_column.collation).must_equal "SQL_Latin1_General_CP1_CI_AS"
32+
end
33+
end

test/cases/migration_test_sqlserver.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
6363
it "change null and default" do
6464
assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil }
6565
end
66+
67+
it "change collation" do
68+
assert_nothing_raised { connection.change_column :sst_string_collation, :string_with_collation, :varchar, collation: :SQL_Latin1_General_CP437_BIN }
69+
70+
SstStringCollation.reset_column_information
71+
assert_equal "SQL_Latin1_General_CP437_BIN", SstStringCollation.columns_hash['string_with_collation'].collation
72+
end
6673
end
6774

6875
describe "#create_schema" do

test/cases/schema_dumper_test_sqlserver.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
119119
assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil
120120
end
121121

122+
it "dump column collation" do
123+
generate_schema_for_table('sst_string_collation')
124+
125+
assert_line :string_without_collation, type: "string", limit: nil, default: nil, collation: nil
126+
assert_line :string_default_collation, type: "varchar", limit: nil, default: nil, collation: nil
127+
assert_line :string_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
128+
assert_line :varchar_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS"
129+
end
130+
122131
# Special Cases
123132

124133
it "honor nonstandard primary keys" do
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
class CreateClientsAndChangeColumnCollation < ActiveRecord::Migration[5.2]
4+
def up
5+
create_table :clients do |t|
6+
t.string :name
7+
t.string :code, collation: :SQL_Latin1_General_CP1_CS_AS
8+
9+
t.timestamps
10+
end
11+
12+
change_column :clients, :name, :string, collation: 'SQL_Latin1_General_CP1_CS_AS'
13+
change_column :clients, :code, :string, collation: 'SQL_Latin1_General_CP1_CI_AS'
14+
end
15+
16+
def down
17+
drop_table :clients
18+
end
19+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class SstStringCollation < ActiveRecord::Base
2+
self.table_name = "sst_string_collation"
3+
end

test/schema/sqlserver_specific_schema.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@
9595
t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line."
9696
end
9797

98+
create_table :sst_string_collation, collation: :SQL_Latin1_General_CP1_CI_AS, force: true do |t|
99+
t.string :string_without_collation
100+
t.varchar :string_default_collation, collation: :SQL_Latin1_General_CP1_CI_AS
101+
t.varchar :string_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS
102+
t.varchar :varchar_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS
103+
end
104+
98105
create_table :sst_edge_schemas, force: true do |t|
99106
t.string :description
100107
t.column "crazy]]quote", :string

0 commit comments

Comments
 (0)