activerecord-sqlserver-adapter 6.0.0.rc1 → 6.1.0.0.rc1
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 +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +18 -23
- data/Gemfile +11 -5
- data/Guardfile +9 -8
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/Rakefile +12 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +4 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -13
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +8 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +58 -43
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +15 -15
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -10
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +131 -105
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +25 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -5
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +6 -10
- data/lib/active_record/connection_adapters/sqlserver/type.rb +36 -35
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
- data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +170 -136
- data/lib/active_record/connection_adapters/sqlserver_column.rb +16 -1
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +36 -39
- data/lib/activerecord-sqlserver-adapter.rb +1 -1
- data/lib/arel/visitors/sqlserver.rb +126 -50
- data/lib/arel_sqlserver.rb +2 -2
- data/test/cases/adapter_test_sqlserver.rb +203 -190
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +12 -12
- data/test/cases/coerced_tests.rb +656 -318
- data/test/cases/column_test_sqlserver.rb +285 -284
- data/test/cases/connection_test_sqlserver.rb +15 -20
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +26 -19
- data/test/cases/fetch_test_sqlserver.rb +14 -22
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
- data/test/cases/helper_sqlserver.rb +13 -15
- data/test/cases/in_clause_test_sqlserver.rb +36 -9
- data/test/cases/index_test_sqlserver.rb +13 -15
- data/test/cases/json_test_sqlserver.rb +23 -25
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +71 -26
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +57 -53
- data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +33 -46
- data/test/cases/schema_dumper_test_sqlserver.rb +121 -108
- data/test/cases/schema_test_sqlserver.rb +18 -26
- data/test/cases/scratchpad_test_sqlserver.rb +2 -4
- data/test/cases/showplan_test_sqlserver.rb +24 -33
- data/test/cases/specific_schema_test_sqlserver.rb +66 -65
- data/test/cases/transaction_test_sqlserver.rb +16 -19
- data/test/cases/trigger_test_sqlserver.rb +12 -12
- data/test/cases/utils_test_sqlserver.rb +68 -70
- data/test/cases/uuid_test_sqlserver.rb +11 -13
- data/test/debug.rb +6 -6
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +1 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
- data/test/models/sqlserver/booking.rb +1 -1
- data/test/models/sqlserver/customers_view.rb +1 -1
- data/test/models/sqlserver/dollar_table_name.rb +1 -1
- data/test/models/sqlserver/edge_schema.rb +1 -3
- data/test/models/sqlserver/fk_has_fk.rb +1 -1
- data/test/models/sqlserver/fk_has_pk.rb +1 -1
- data/test/models/sqlserver/natural_pk_data.rb +2 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
- data/test/models/sqlserver/no_pk_data.rb +1 -1
- data/test/models/sqlserver/object_default.rb +1 -1
- data/test/models/sqlserver/quoted_table.rb +2 -2
- data/test/models/sqlserver/quoted_view_1.rb +1 -1
- data/test/models/sqlserver/quoted_view_2.rb +1 -1
- data/test/models/sqlserver/sst_memory.rb +1 -1
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +1 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
- data/test/models/sqlserver/string_defaults_view.rb +1 -1
- data/test/models/sqlserver/tinyint_pk.rb +1 -1
- data/test/models/sqlserver/trigger.rb +2 -2
- data/test/models/sqlserver/trigger_history.rb +1 -1
- data/test/models/sqlserver/upper.rb +1 -1
- data/test/models/sqlserver/uppered.rb +1 -1
- data/test/models/sqlserver/uuid.rb +1 -1
- data/test/schema/sqlserver_specific_schema.rb +36 -21
- data/test/support/coerceable_test_sqlserver.rb +1 -4
- data/test/support/connection_reflection.rb +1 -2
- data/test/support/core_ext/query_cache.rb +1 -1
- data/test/support/load_schema_sqlserver.rb +3 -5
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/minitest_sqlserver.rb +1 -1
- data/test/support/paths_sqlserver.rb +9 -11
- data/test/support/rake_helpers.rb +12 -10
- data/test/support/sql_counter_sqlserver.rb +14 -16
- data/test/support/test_in_memory_oltp.rb +7 -7
- metadata +31 -11
- data/.travis.yml +0 -23
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
4
|
|
|
5
5
|
class IndexTestSQLServer < ActiveRecord::TestCase
|
|
6
|
-
|
|
7
6
|
before do
|
|
8
7
|
connection.create_table(:testings) do |t|
|
|
9
8
|
t.column :foo, :string, limit: 100
|
|
@@ -19,31 +18,30 @@ class IndexTestSQLServer < ActiveRecord::TestCase
|
|
|
19
18
|
connection.drop_table :testings rescue nil
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
it
|
|
21
|
+
it "add index with order" do
|
|
23
22
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do
|
|
24
|
-
connection.add_index
|
|
25
|
-
connection.remove_index
|
|
23
|
+
connection.add_index "testings", ["last_name"], order: { last_name: :desc }
|
|
24
|
+
connection.remove_index "testings", ["last_name"]
|
|
26
25
|
end
|
|
27
26
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do
|
|
28
|
-
connection.add_index
|
|
29
|
-
connection.remove_index
|
|
27
|
+
connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc }
|
|
28
|
+
connection.remove_index "testings", ["last_name", "first_name"]
|
|
30
29
|
end
|
|
31
30
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do
|
|
32
|
-
connection.add_index
|
|
33
|
-
connection.remove_index
|
|
31
|
+
connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc }
|
|
32
|
+
connection.remove_index "testings", ["last_name", "first_name"]
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
it
|
|
36
|
+
it "add index with where" do
|
|
38
37
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do
|
|
39
|
-
connection.add_index
|
|
40
|
-
connection.remove_index
|
|
38
|
+
connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'"
|
|
39
|
+
connection.remove_index "testings", "last_name"
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
it
|
|
43
|
+
it "add index with expression" do
|
|
45
44
|
connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])"
|
|
46
|
-
connection.add_index
|
|
45
|
+
connection.add_index "testings", "first_name_upper"
|
|
47
46
|
end
|
|
48
|
-
|
|
49
47
|
end
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
4
|
|
|
5
5
|
if ActiveRecord::Base.connection.supports_json?
|
|
6
|
-
class JsonTestSQLServer < ActiveRecord::TestCase
|
|
6
|
+
class JsonTestSQLServer < ActiveRecord::TestCase
|
|
7
|
+
before do
|
|
8
|
+
@o1 = SSTestDatatypeMigrationJson.create! json_col: { "a" => "a", "b" => "b", "c" => "c" }
|
|
9
|
+
@o2 = SSTestDatatypeMigrationJson.create! json_col: { "a" => nil, "b" => "b", "c" => "c" }
|
|
10
|
+
@o3 = SSTestDatatypeMigrationJson.create! json_col: { "x" => 1, "y" => 2, "z" => 3 }
|
|
11
|
+
@o4 = SSTestDatatypeMigrationJson.create! json_col: { "array" => [1, 2, 3] }
|
|
12
|
+
@o5 = SSTestDatatypeMigrationJson.create! json_col: nil
|
|
13
|
+
end
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
it 'can return and save JSON data' do
|
|
17
|
-
_(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({ 'a' => 'a', 'b' => 'b', 'c' => 'c' })
|
|
18
|
-
@o1.json_col = { 'a' => 'a' }
|
|
19
|
-
_(@o1.json_col).must_equal({ 'a' => 'a' })
|
|
20
|
-
@o1.save!
|
|
21
|
-
_(@o1.reload.json_col).must_equal({ 'a' => 'a' })
|
|
22
|
-
end
|
|
15
|
+
it "can return and save JSON data" do
|
|
16
|
+
_(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({ "a" => "a", "b" => "b", "c" => "c" })
|
|
17
|
+
@o1.json_col = { "a" => "a" }
|
|
18
|
+
_(@o1.json_col).must_equal({ "a" => "a" })
|
|
19
|
+
@o1.save!
|
|
20
|
+
_(@o1.reload.json_col).must_equal({ "a" => "a" })
|
|
21
|
+
end
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
it "can use ISJSON function" do
|
|
24
|
+
_(SSTestDatatypeMigrationJson.where("ISJSON(json_col) > 0").count).must_equal 4
|
|
25
|
+
_(SSTestDatatypeMigrationJson.where("ISJSON(json_col) IS NULL").count).must_equal 1
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
it "can use JSON_VALUE function" do
|
|
29
|
+
_(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2
|
|
30
|
+
end
|
|
31
31
|
end
|
|
32
|
-
|
|
33
|
-
end
|
|
34
32
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/post"
|
|
5
|
+
require "models/author"
|
|
6
|
+
|
|
7
|
+
class LateralTestSQLServer < ActiveRecord::TestCase
|
|
8
|
+
fixtures :posts, :authors
|
|
9
|
+
|
|
10
|
+
it 'uses OUTER APPLY for OUTER JOIN LATERAL' do
|
|
11
|
+
post = Arel::Table.new(:posts)
|
|
12
|
+
author = Arel::Table.new(:authors)
|
|
13
|
+
subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42))
|
|
14
|
+
|
|
15
|
+
one = Arel::Nodes::Quoted.new(1)
|
|
16
|
+
eq = Arel::Nodes::Equality.new(one, one)
|
|
17
|
+
|
|
18
|
+
sql = author.project(Arel.star).where(author[:name].matches("David")).outer_join(subselect.lateral.as("bar")).on(eq).to_sql
|
|
19
|
+
results = ActiveRecord::Base.connection.exec_query sql
|
|
20
|
+
assert_equal sql, "SELECT * FROM [authors] OUTER APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
|
|
21
|
+
assert_equal results.length, 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'uses CROSS APPLY for INNER JOIN LATERAL' do
|
|
25
|
+
post = Arel::Table.new(:posts)
|
|
26
|
+
author = Arel::Table.new(:authors)
|
|
27
|
+
subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42))
|
|
28
|
+
|
|
29
|
+
sql = author.project(Arel.star).where(author[:name].matches("David")).join(subselect.lateral.as("bar")).to_sql
|
|
30
|
+
results = ActiveRecord::Base.connection.exec_query sql
|
|
31
|
+
|
|
32
|
+
assert_equal sql, "SELECT * FROM [authors] CROSS APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'"
|
|
33
|
+
assert_equal results.length, 0
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/person"
|
|
5
5
|
|
|
6
6
|
class MigrationTestSQLServer < ActiveRecord::TestCase
|
|
7
|
-
|
|
8
|
-
describe 'For transactions' do
|
|
9
|
-
|
|
7
|
+
describe "For transactions" do
|
|
10
8
|
before do
|
|
11
|
-
@trans_test_table1 =
|
|
12
|
-
@trans_test_table2 =
|
|
13
|
-
@trans_tables = [@trans_test_table1
|
|
9
|
+
@trans_test_table1 = "sqlserver_trans_table1"
|
|
10
|
+
@trans_test_table2 = "sqlserver_trans_table2"
|
|
11
|
+
@trans_tables = [@trans_test_table1, @trans_test_table2]
|
|
14
12
|
end
|
|
15
13
|
|
|
16
14
|
after do
|
|
@@ -19,9 +17,9 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
|
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
it
|
|
20
|
+
it "not create a tables if error in migrations" do
|
|
23
21
|
begin
|
|
24
|
-
migrations_dir = File.join ARTest::SQLServer.migrations_root,
|
|
22
|
+
migrations_dir = File.join ARTest::SQLServer.migrations_root, "transaction_table"
|
|
25
23
|
quietly { ActiveRecord::MigrationContext.new(migrations_dir, ActiveRecord::SchemaMigration).up }
|
|
26
24
|
rescue Exception => e
|
|
27
25
|
assert_match %r|this and all later migrations canceled|, e.message
|
|
@@ -29,45 +27,92 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
|
|
|
29
27
|
_(connection.tables).wont_include @trans_test_table1
|
|
30
28
|
_(connection.tables).wont_include @trans_test_table2
|
|
31
29
|
end
|
|
32
|
-
|
|
33
30
|
end
|
|
34
31
|
|
|
35
|
-
describe
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
lock_version_column = Person.columns_hash['lock_version']
|
|
32
|
+
describe "For changing column" do
|
|
33
|
+
it "not raise exception when column contains default constraint" do
|
|
34
|
+
lock_version_column = Person.columns_hash["lock_version"]
|
|
39
35
|
assert_equal :integer, lock_version_column.type
|
|
40
36
|
assert lock_version_column.default.present?
|
|
41
|
-
assert_nothing_raised { connection.change_column
|
|
37
|
+
assert_nothing_raised { connection.change_column "people", "lock_version", :string }
|
|
42
38
|
Person.reset_column_information
|
|
43
|
-
lock_version_column = Person.columns_hash[
|
|
39
|
+
lock_version_column = Person.columns_hash["lock_version"]
|
|
44
40
|
assert_equal :string, lock_version_column.type
|
|
45
41
|
assert lock_version_column.default.nil?
|
|
46
|
-
assert_nothing_raised { connection.change_column
|
|
42
|
+
assert_nothing_raised { connection.change_column "people", "lock_version", :integer }
|
|
47
43
|
Person.reset_column_information
|
|
48
44
|
end
|
|
49
45
|
|
|
50
|
-
it
|
|
46
|
+
it "not drop the default constraint if just renaming" do
|
|
51
47
|
find_default = lambda do
|
|
52
|
-
connection.execute_procedure(:sp_helpconstraint,
|
|
53
|
-
row[
|
|
48
|
+
connection.execute_procedure(:sp_helpconstraint, "sst_string_defaults", "nomsg").select do |row|
|
|
49
|
+
row["constraint_type"] == "DEFAULT on column string_with_pretend_paren_three"
|
|
54
50
|
end.last
|
|
55
51
|
end
|
|
56
52
|
default_before = find_default.call
|
|
57
53
|
connection.change_column :sst_string_defaults, :string_with_pretend_paren_three, :string, limit: 255
|
|
58
54
|
default_after = find_default.call
|
|
59
55
|
assert default_after
|
|
60
|
-
assert_equal default_before[
|
|
56
|
+
assert_equal default_before["constraint_keys"], default_after["constraint_keys"]
|
|
61
57
|
end
|
|
62
|
-
|
|
63
|
-
it
|
|
58
|
+
|
|
59
|
+
it "change limit" do
|
|
64
60
|
assert_nothing_raised { connection.change_column :people, :lock_version, :integer, limit: 8 }
|
|
65
61
|
end
|
|
66
|
-
|
|
67
|
-
it
|
|
62
|
+
|
|
63
|
+
it "change null and default" do
|
|
68
64
|
assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil }
|
|
69
65
|
end
|
|
70
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
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
describe "#create_schema" do
|
|
76
|
+
it "creates a new schema" do
|
|
77
|
+
connection.create_schema("some schema")
|
|
78
|
+
|
|
79
|
+
schemas = connection.exec_query("select name from sys.schemas").to_a
|
|
80
|
+
|
|
81
|
+
assert_includes schemas, { "name" => "some schema" }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "creates a new schema with an owner" do
|
|
85
|
+
connection.create_schema("some schema", :guest)
|
|
86
|
+
|
|
87
|
+
schemas = connection.exec_query("select name, principal_id from sys.schemas").to_a
|
|
88
|
+
|
|
89
|
+
assert_includes schemas, { "name" => "some schema", "principal_id" => 2 }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "#change_table_schema" do
|
|
94
|
+
before { connection.create_schema("foo") }
|
|
95
|
+
|
|
96
|
+
it "transfer the given table to the given schema" do
|
|
97
|
+
connection.change_table_schema("foo", "orders")
|
|
98
|
+
|
|
99
|
+
assert connection.data_source_exists?("foo.orders")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe "#drop_schema" do
|
|
104
|
+
before { connection.create_schema("some schema") }
|
|
105
|
+
|
|
106
|
+
it "drops a schema" do
|
|
107
|
+
schemas = connection.exec_query("select name from sys.schemas").to_a
|
|
108
|
+
|
|
109
|
+
assert_includes schemas, { "name" => "some schema" }
|
|
110
|
+
|
|
111
|
+
connection.drop_schema("some schema")
|
|
112
|
+
|
|
113
|
+
schemas = connection.exec_query("select name from sys.schemas").to_a
|
|
114
|
+
|
|
115
|
+
refute_includes schemas, { "name" => "some schema" }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
73
118
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/company"
|
|
5
|
+
|
|
6
|
+
class OptimizerHitsTestSQLServer < ActiveRecord::TestCase
|
|
7
|
+
fixtures :companies
|
|
8
|
+
|
|
9
|
+
it "apply optimizations" do
|
|
10
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
|
|
11
|
+
companies = Company.optimizer_hints("HASH GROUP")
|
|
12
|
+
companies = companies.distinct.select("firm_id")
|
|
13
|
+
assert_includes companies.explain, "| Hash Match | Aggregate |"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do
|
|
17
|
+
companies = Company.optimizer_hints("ORDER GROUP")
|
|
18
|
+
companies = companies.distinct.select("firm_id")
|
|
19
|
+
assert_includes companies.explain, "| Stream Aggregate | Aggregate |"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "apply multiple optimizations" do
|
|
24
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do
|
|
25
|
+
companies = Company.optimizer_hints("HASH GROUP", "FAST 1")
|
|
26
|
+
companies = companies.distinct.select("firm_id")
|
|
27
|
+
assert_includes companies.explain, "| Hash Match | Flow Distinct |"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "support subqueries" do
|
|
32
|
+
assert_sql(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do
|
|
33
|
+
companies = Company.optimizer_hints("MAXDOP 2")
|
|
34
|
+
companies = companies.select(:id).where(firm_id: [0, 1]).limit(3)
|
|
35
|
+
assert_equal 3, companies.count
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "sanitize values" do
|
|
40
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
|
|
41
|
+
companies = Company.optimizer_hints("OPTION (HASH GROUP)")
|
|
42
|
+
companies = companies.distinct.select("firm_id")
|
|
43
|
+
companies.to_a
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
|
|
47
|
+
companies = Company.optimizer_hints("OPTION(HASH GROUP)")
|
|
48
|
+
companies = companies.distinct.select("firm_id")
|
|
49
|
+
companies.to_a
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(TABLE HINT \(\[companies\], INDEX\(1\)\)\)\z}) do
|
|
53
|
+
companies = Company.optimizer_hints("OPTION(TABLE HINT ([companies], INDEX(1)))")
|
|
54
|
+
companies = companies.distinct.select("firm_id")
|
|
55
|
+
companies.to_a
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do
|
|
59
|
+
companies = Company.optimizer_hints("Option(HASH GROUP)")
|
|
60
|
+
companies = companies.distinct.select("firm_id")
|
|
61
|
+
companies.to_a
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "skip optimization after unscope" do
|
|
66
|
+
assert_sql("SELECT DISTINCT [companies].[firm_id] FROM [companies]") do
|
|
67
|
+
companies = Company.optimizer_hints("HASH GROUP")
|
|
68
|
+
companies = companies.distinct.select("firm_id")
|
|
69
|
+
companies.unscope(:optimizer_hints).load
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -1,149 +1,153 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/post"
|
|
5
5
|
|
|
6
6
|
class OrderTestSQLServer < ActiveRecord::TestCase
|
|
7
|
-
|
|
8
7
|
fixtures :posts
|
|
9
8
|
|
|
10
|
-
it
|
|
9
|
+
it "not mangel complex order clauses" do
|
|
11
10
|
xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END"
|
|
12
|
-
xyz_post = Post.create title:
|
|
11
|
+
xyz_post = Post.create title: "XYZ Post", body: "Test cased orders."
|
|
13
12
|
assert_equal xyz_post, Post.order(Arel.sql(xyz_order)).first
|
|
14
13
|
end
|
|
15
14
|
|
|
16
|
-
it
|
|
15
|
+
it "support column" do
|
|
17
16
|
order = "title"
|
|
18
|
-
post1 = Post.create title:
|
|
17
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
19
18
|
assert_equal post1, Post.order(order).first
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
it
|
|
21
|
+
it "support column ASC" do
|
|
23
22
|
order = "title ASC"
|
|
24
|
-
post1 = Post.create title:
|
|
23
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
25
24
|
assert_equal post1, Post.order(order).first
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
it
|
|
27
|
+
it "support column DESC" do
|
|
29
28
|
order = "title DESC"
|
|
30
|
-
post1 = Post.create title:
|
|
29
|
+
post1 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
31
30
|
assert_equal post1, Post.order(order).first
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
it
|
|
33
|
+
it "support column as symbol" do
|
|
35
34
|
order = :title
|
|
36
|
-
post1 = Post.create title:
|
|
35
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
37
36
|
assert_equal post1, Post.order(order).first
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
it
|
|
39
|
+
it "support table and column" do
|
|
41
40
|
order = "posts.title"
|
|
42
|
-
post1 = Post.create title:
|
|
41
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
43
42
|
assert_equal post1, Post.order(order).first
|
|
44
43
|
end
|
|
45
44
|
|
|
46
|
-
it
|
|
45
|
+
it "support quoted column" do
|
|
47
46
|
order = "[title]"
|
|
48
|
-
post1 = Post.create title:
|
|
47
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
49
48
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
50
49
|
end
|
|
51
50
|
|
|
52
|
-
it
|
|
51
|
+
it "support quoted table and column" do
|
|
53
52
|
order = "[posts].[title]"
|
|
54
|
-
post1 = Post.create title:
|
|
53
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
55
54
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
it
|
|
57
|
+
it "support primary: column, secondary: column" do
|
|
59
58
|
order = "title DESC, body"
|
|
60
|
-
post1 = Post.create title:
|
|
61
|
-
post2 = Post.create title:
|
|
59
|
+
post1 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
60
|
+
post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders."
|
|
62
61
|
assert_equal post1, Post.order(order).first
|
|
63
62
|
assert_equal post2, Post.order(order).second
|
|
64
63
|
end
|
|
65
64
|
|
|
66
|
-
it
|
|
65
|
+
it "support primary: table and column, secondary: column" do
|
|
67
66
|
order = "posts.title DESC, body"
|
|
68
|
-
post1 = Post.create title:
|
|
69
|
-
post2 = Post.create title:
|
|
67
|
+
post1 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
68
|
+
post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders."
|
|
70
69
|
assert_equal post1, Post.order(order).first
|
|
71
70
|
assert_equal post2, Post.order(order).second
|
|
72
71
|
end
|
|
73
72
|
|
|
74
|
-
it
|
|
73
|
+
it "support primary: case expression, secondary: column" do
|
|
75
74
|
order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body"
|
|
76
|
-
post1 = Post.create title:
|
|
77
|
-
post2 = Post.create title:
|
|
75
|
+
post1 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
76
|
+
post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders."
|
|
78
77
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
79
78
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
80
79
|
end
|
|
81
80
|
|
|
82
|
-
it
|
|
81
|
+
it "support primary: quoted table and column, secondary: case expresion" do
|
|
83
82
|
order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC"
|
|
84
|
-
post1 = Post.create title:
|
|
85
|
-
post2 = Post.create title:
|
|
83
|
+
post1 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders."
|
|
84
|
+
post2 = Post.create title: "ZZY Post", body: "ZZZ Test cased orders."
|
|
86
85
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
87
86
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
it
|
|
89
|
+
it "support inline function" do
|
|
91
90
|
order = "LEN(title)"
|
|
92
|
-
post1 = Post.create title:
|
|
91
|
+
post1 = Post.create title: "A", body: "AAA Test cased orders."
|
|
93
92
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
94
93
|
end
|
|
95
94
|
|
|
96
|
-
it
|
|
95
|
+
it "support inline function with parameters" do
|
|
97
96
|
order = "SUBSTRING(title, 1, 3)"
|
|
98
|
-
post1 = Post.create title:
|
|
97
|
+
post1 = Post.create title: "AAA Post", body: "Test cased orders."
|
|
99
98
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
it
|
|
101
|
+
it "support inline function with parameters DESC" do
|
|
103
102
|
order = "SUBSTRING(title, 1, 3) DESC"
|
|
104
|
-
post1 = Post.create title:
|
|
103
|
+
post1 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
105
104
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
106
105
|
end
|
|
107
106
|
|
|
108
|
-
it
|
|
107
|
+
it "support primary: inline function, secondary: column" do
|
|
109
108
|
order = "LEN(title), body"
|
|
110
|
-
post1 = Post.create title:
|
|
111
|
-
post2 = Post.create title:
|
|
109
|
+
post1 = Post.create title: "A", body: "AAA Test cased orders."
|
|
110
|
+
post2 = Post.create title: "A", body: "Test cased orders."
|
|
112
111
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
113
112
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
114
113
|
end
|
|
115
114
|
|
|
116
|
-
it
|
|
115
|
+
it "support primary: inline function, secondary: column with direction" do
|
|
117
116
|
order = "LEN(title) ASC, body DESC"
|
|
118
|
-
post1 = Post.create title:
|
|
119
|
-
post2 = Post.create title:
|
|
117
|
+
post1 = Post.create title: "A", body: "ZZZ Test cased orders."
|
|
118
|
+
post2 = Post.create title: "A", body: "Test cased orders."
|
|
120
119
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
121
120
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
122
121
|
end
|
|
123
122
|
|
|
124
|
-
it
|
|
123
|
+
it "support primary: column, secondary: inline function" do
|
|
125
124
|
order = "body DESC, LEN(title)"
|
|
126
|
-
post1 = Post.create title:
|
|
127
|
-
post2 = Post.create title:
|
|
125
|
+
post1 = Post.create title: "Post", body: "ZZZ Test cased orders."
|
|
126
|
+
post2 = Post.create title: "Longer Post", body: "ZZZ Test cased orders."
|
|
128
127
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
129
128
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
130
129
|
end
|
|
131
130
|
|
|
132
|
-
it
|
|
131
|
+
it "support primary: case expression, secondary: inline function" do
|
|
133
132
|
order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC"
|
|
134
|
-
post1 = Post.create title:
|
|
135
|
-
post2 = Post.create title:
|
|
133
|
+
post1 = Post.create title: "ZZZ Post", body: "Z"
|
|
134
|
+
post2 = Post.create title: "ZZZ Post", body: "Test cased orders."
|
|
136
135
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
137
136
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
138
137
|
end
|
|
139
138
|
|
|
140
|
-
it
|
|
139
|
+
it "support primary: inline function, secondary: case expression" do
|
|
141
140
|
order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC"
|
|
142
|
-
post1 = Post.create title:
|
|
143
|
-
post2 = Post.create title:
|
|
141
|
+
post1 = Post.create title: "ZZZ Post", body: "Z"
|
|
142
|
+
post2 = Post.create title: "Post", body: "Z"
|
|
144
143
|
assert_equal post1, Post.order(Arel.sql(order)).first
|
|
145
144
|
assert_equal post2, Post.order(Arel.sql(order)).second
|
|
146
145
|
end
|
|
147
146
|
|
|
148
|
-
|
|
147
|
+
# Executing this kind of queries will raise "A column has been specified more than once in the order by list"
|
|
148
|
+
# This test shows that we don't do anything to prevent this
|
|
149
|
+
it "doesn't deduplicate semantically equal orders" do
|
|
150
|
+
sql = Post.order(:id).order("posts.id ASC").to_sql
|
|
151
|
+
assert_equal "SELECT [posts].* FROM [posts] ORDER BY [posts].[id] ASC, posts.id ASC", sql
|
|
152
|
+
end
|
|
149
153
|
end
|