activerecord-sqlserver-adapter 5.2.1 → 7.0.0.0
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/.editorconfig +9 -0
- data/.github/issue_template.md +23 -0
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +17 -27
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +49 -41
- data/Guardfile +9 -8
- data/MIT-LICENSE +1 -1
- data/README.md +65 -42
- data/RUNNING_UNIT_TESTS.md +3 -0
- data/Rakefile +14 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +25 -14
- data/appveyor.yml +22 -17
- data/docker-compose.ci.yml +7 -5
- data/guides/RELEASING.md +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +51 -14
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
- data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
- data/lib/active_record/sqlserver_base.rb +11 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
- data/lib/activerecord-sqlserver-adapter.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +177 -56
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/active_schema_test_sqlserver.rb +55 -0
- data/test/cases/adapter_test_sqlserver.rb +258 -173
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +1421 -397
- data/test/cases/column_test_sqlserver.rb +321 -315
- data/test/cases/connection_test_sqlserver.rb +17 -20
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
- data/test/cases/fetch_test_sqlserver.rb +33 -21
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
- data/test/cases/helper_sqlserver.rb +15 -15
- data/test/cases/in_clause_test_sqlserver.rb +63 -0
- data/test/cases/index_test_sqlserver.rb +15 -15
- data/test/cases/json_test_sqlserver.rb +25 -25
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +74 -27
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +59 -53
- data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +70 -45
- data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
- data/test/cases/schema_test_sqlserver.rb +20 -26
- data/test/cases/scratchpad_test_sqlserver.rb +4 -4
- data/test/cases/showplan_test_sqlserver.rb +28 -35
- data/test/cases/specific_schema_test_sqlserver.rb +68 -65
- data/test/cases/transaction_test_sqlserver.rb +18 -20
- data/test/cases/trigger_test_sqlserver.rb +14 -13
- data/test/cases/utils_test_sqlserver.rb +70 -70
- data/test/cases/uuid_test_sqlserver.rb +13 -14
- data/test/debug.rb +8 -6
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +3 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
- data/test/models/sqlserver/booking.rb +3 -1
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/customers_view.rb +3 -1
- data/test/models/sqlserver/datatype.rb +2 -0
- data/test/models/sqlserver/datatype_migration.rb +2 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -1
- data/test/models/sqlserver/edge_schema.rb +3 -3
- data/test/models/sqlserver/fk_has_fk.rb +3 -1
- data/test/models/sqlserver/fk_has_pk.rb +3 -1
- data/test/models/sqlserver/natural_pk_data.rb +4 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
- data/test/models/sqlserver/no_pk_data.rb +3 -1
- data/test/models/sqlserver/object_default.rb +3 -1
- data/test/models/sqlserver/quoted_table.rb +4 -2
- data/test/models/sqlserver/quoted_view_1.rb +3 -1
- data/test/models/sqlserver/quoted_view_2.rb +3 -1
- data/test/models/sqlserver/sst_memory.rb +3 -1
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
- data/test/models/sqlserver/string_defaults_view.rb +3 -1
- data/test/models/sqlserver/tinyint_pk.rb +3 -1
- data/test/models/sqlserver/trigger.rb +4 -2
- data/test/models/sqlserver/trigger_history.rb +3 -1
- data/test/models/sqlserver/upper.rb +3 -1
- data/test/models/sqlserver/uppered.rb +3 -1
- data/test/models/sqlserver/uuid.rb +3 -1
- data/test/schema/sqlserver_specific_schema.rb +56 -21
- data/test/support/coerceable_test_sqlserver.rb +19 -13
- data/test/support/connection_reflection.rb +3 -2
- data/test/support/core_ext/query_cache.rb +4 -1
- data/test/support/load_schema_sqlserver.rb +5 -5
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/minitest_sqlserver.rb +3 -1
- data/test/support/paths_sqlserver.rb +11 -11
- data/test/support/rake_helpers.rb +15 -10
- data/test/support/sql_counter_sqlserver.rb +16 -15
- data/test/support/test_in_memory_oltp.rb +9 -7
- metadata +47 -13
- data/.travis.yml +0 -25
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -26
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
require 'models/reply'
|
|
3
|
-
require 'models/topic'
|
|
1
|
+
# frozen_string_literal: true
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/reply"
|
|
5
|
+
require "models/topic"
|
|
6
6
|
|
|
7
|
+
class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
7
8
|
self.use_transactional_tests = false
|
|
8
9
|
|
|
9
10
|
fixtures :topics, :accounts
|
|
@@ -13,7 +14,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
|
13
14
|
assert connection.active?
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
it
|
|
17
|
+
it "affect rows" do
|
|
17
18
|
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
|
|
18
19
|
updated = Topic.update(topic_data.keys, topic_data.values)
|
|
19
20
|
assert_equal 2, updated.size
|
|
@@ -22,43 +23,40 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
|
22
23
|
assert_equal 2, Topic.delete([1, 2])
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
it
|
|
26
|
-
assert_equal
|
|
26
|
+
it "allow usage of :database connection option to remove setting from dsn" do
|
|
27
|
+
assert_equal "activerecord_unittest", connection.current_database
|
|
27
28
|
begin
|
|
28
|
-
connection.use_database(
|
|
29
|
-
assert_equal
|
|
29
|
+
connection.use_database("activerecord_unittest2")
|
|
30
|
+
assert_equal "activerecord_unittest2", connection.current_database
|
|
30
31
|
ensure
|
|
31
32
|
connection.use_database
|
|
32
|
-
assert_equal
|
|
33
|
+
assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options"
|
|
33
34
|
end
|
|
34
35
|
end unless connection_sqlserver_azure?
|
|
35
36
|
|
|
36
|
-
describe
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
_(['Fixnum', 'Integer']).must_include connection.spid.class.name
|
|
37
|
+
describe "Connection management" do
|
|
38
|
+
it "set spid on connect" do
|
|
39
|
+
_(["Fixnum", "Integer"]).must_include connection.spid.class.name
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
it
|
|
42
|
+
it "reset spid on disconnect!" do
|
|
43
43
|
connection.disconnect!
|
|
44
44
|
assert connection.spid.nil?
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
it
|
|
47
|
+
it "reset the connection" do
|
|
48
48
|
connection.disconnect!
|
|
49
49
|
_(connection.raw_connection).must_be_nil
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
it
|
|
52
|
+
it "be able to disconnect and reconnect at will" do
|
|
53
53
|
disconnect_raw_connection!
|
|
54
54
|
assert !connection.active?
|
|
55
55
|
connection.reconnect!
|
|
56
56
|
assert connection.active?
|
|
57
57
|
end
|
|
58
|
-
|
|
59
58
|
end
|
|
60
59
|
|
|
61
|
-
|
|
62
60
|
private
|
|
63
61
|
|
|
64
62
|
def disconnect_raw_connection!
|
|
@@ -67,5 +65,4 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
|
67
65
|
connection.raw_connection.close rescue nil
|
|
68
66
|
end
|
|
69
67
|
end
|
|
70
|
-
|
|
71
68
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
|
|
5
|
+
class TestDisconnectedAdapter < ActiveRecord::TestCase
|
|
6
|
+
self.use_transactional_tests = false
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
@connection = ActiveRecord::Base.connection
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
teardown do
|
|
13
|
+
return if in_memory_db?
|
|
14
|
+
db_config = ActiveRecord::Base.connection_db_config
|
|
15
|
+
ActiveRecord::Base.establish_connection(db_config)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "can't execute procedures while disconnected" do
|
|
19
|
+
@connection.execute_procedure :sp_tables, "sst_datatypes"
|
|
20
|
+
@connection.disconnect!
|
|
21
|
+
assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
|
|
22
|
+
@connection.execute_procedure :sp_tables, "sst_datatypes"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test "can't execute query while disconnected" do
|
|
27
|
+
sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
|
|
28
|
+
binds = [
|
|
29
|
+
ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
|
|
30
|
+
ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new)
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
@connection.exec_query sql, "TEST", binds
|
|
34
|
+
@connection.disconnect!
|
|
35
|
+
assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
|
|
36
|
+
@connection.exec_query sql, "TEST", binds
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "cases/helper_sqlserver"
|
|
2
|
+
require "models/citation"
|
|
3
|
+
require "models/book"
|
|
4
|
+
|
|
5
|
+
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
6
|
+
fixtures :citations
|
|
7
|
+
|
|
8
|
+
def test_batch_preloading_too_many_ids
|
|
9
|
+
in_clause_length = 10_000
|
|
10
|
+
|
|
11
|
+
# We Monkey patch Preloader to work with batches of 10_000 records.
|
|
12
|
+
# Expect: N Books queries + Citation query
|
|
13
|
+
expected_query_count = (Citation.count / in_clause_length.to_f).ceil + 1
|
|
14
|
+
assert_queries(expected_query_count) do
|
|
15
|
+
Citation.preload(:reference_of).to_a.size
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,44 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
|
|
6
|
+
it "execute a simple procedure" do
|
|
6
7
|
tables = ActiveRecord::Base.execute_procedure :sp_tables
|
|
7
8
|
assert_instance_of Array, tables
|
|
8
9
|
assert tables.first.respond_to?(:keys)
|
|
9
10
|
end
|
|
10
11
|
|
|
11
|
-
it
|
|
12
|
-
tables = ActiveRecord::Base.execute_procedure :sp_tables,
|
|
12
|
+
it "take parameter arguments" do
|
|
13
|
+
tables = ActiveRecord::Base.execute_procedure :sp_tables, "sst_datatypes"
|
|
13
14
|
table_info = tables.first
|
|
14
15
|
assert_equal 1, tables.size
|
|
15
|
-
assert_equal (ENV[
|
|
16
|
-
assert_equal
|
|
16
|
+
assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}"
|
|
17
|
+
assert_equal "TABLE", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}"
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
it
|
|
20
|
-
results1, results2 = ActiveRecord::Base.execute_procedure(
|
|
20
|
+
it "allow multiple result sets to be returned" do
|
|
21
|
+
results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint", "accounts")
|
|
21
22
|
assert_instance_of Array, results1
|
|
22
23
|
assert results1.first.respond_to?(:keys)
|
|
23
|
-
assert results1.first[
|
|
24
|
+
assert results1.first["Object Name"]
|
|
24
25
|
assert_instance_of Array, results2
|
|
25
26
|
assert results2.first.respond_to?(:keys)
|
|
26
|
-
assert results2.first[
|
|
27
|
-
assert results2.first[
|
|
27
|
+
assert results2.first["constraint_name"]
|
|
28
|
+
assert results2.first["constraint_type"]
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
it
|
|
31
|
-
tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name:
|
|
31
|
+
it "take named parameter arguments" do
|
|
32
|
+
tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: "tables", table_owner: "sys"
|
|
32
33
|
table_info = tables.first
|
|
33
34
|
assert_equal 1, tables.size
|
|
34
|
-
assert_equal (ENV[
|
|
35
|
-
assert_equal
|
|
35
|
+
assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}"
|
|
36
|
+
assert_equal "VIEW", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}"
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
it
|
|
39
|
-
date_proc = connection.execute_procedure(
|
|
40
|
-
date_base = connection.select_value(
|
|
39
|
+
it "uses the proper timezone" do
|
|
40
|
+
date_proc = connection.execute_procedure("my_getutcdate").first["utcdate"]
|
|
41
|
+
date_base = connection.select_value("select GETUTCDATE()")
|
|
41
42
|
assert_equal date_base.change(usec: 0), date_proc.change(usec: 0)
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
it 'test deprecation with transaction return when executing procedure' do
|
|
46
|
+
assert_deprecated do
|
|
47
|
+
ActiveRecord::Base.transaction do
|
|
48
|
+
connection.execute_procedure("my_getutcdate")
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
44
53
|
end
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
require 'models/book'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/book"
|
|
5
5
|
|
|
6
|
+
class FetchTestSqlserver < ActiveRecord::TestCase
|
|
6
7
|
let(:books) { @books }
|
|
7
8
|
|
|
8
9
|
before { create_10_books }
|
|
9
10
|
|
|
10
|
-
it
|
|
11
|
-
books = Book.select(
|
|
12
|
-
assert_equal Book.all[5,3].map(&:id), books.map(&:id)
|
|
11
|
+
it "work with fully qualified table and columns in select" do
|
|
12
|
+
books = Book.select("books.id, books.name").limit(3).offset(5)
|
|
13
|
+
assert_equal Book.all[5, 3].map(&:id), books.map(&:id)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
describe
|
|
16
|
-
|
|
17
|
-
it 'gauntlet' do
|
|
16
|
+
describe "count" do
|
|
17
|
+
it "gauntlet" do
|
|
18
18
|
books[0].destroy
|
|
19
19
|
books[1].destroy
|
|
20
20
|
books[2].destroy
|
|
@@ -29,29 +29,41 @@ class FetchTestSqlserver < ActiveRecord::TestCase
|
|
|
29
29
|
assert_equal 0, Book.limit(3).offset(7).count
|
|
30
30
|
assert_equal 0, Book.limit(3).offset(8).count
|
|
31
31
|
end
|
|
32
|
-
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
describe
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Book.
|
|
39
|
-
_(Book.order(:name).limit(
|
|
40
|
-
_(Book.order(:name).limit(2).offset(
|
|
41
|
-
_(Book.order(:name).limit(
|
|
42
|
-
_(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9']
|
|
34
|
+
describe "order" do
|
|
35
|
+
it "gauntlet" do
|
|
36
|
+
Book.where(name: "Name-10").delete_all
|
|
37
|
+
_(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ["Name-2"]
|
|
38
|
+
_(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ["Name-3", "Name-4"]
|
|
39
|
+
_(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"]
|
|
40
|
+
_(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"]
|
|
43
41
|
_(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal []
|
|
44
42
|
end
|
|
45
|
-
|
|
46
43
|
end
|
|
47
44
|
|
|
48
|
-
|
|
49
45
|
protected
|
|
50
46
|
|
|
51
47
|
def create_10_books
|
|
52
48
|
Book.delete_all
|
|
53
49
|
@books = (1..10).map { |i| Book.create! name: "Name-#{i}" }
|
|
54
50
|
end
|
|
55
|
-
|
|
56
51
|
end
|
|
57
52
|
|
|
53
|
+
class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase
|
|
54
|
+
it "orders by the identity column if table has one" do
|
|
55
|
+
SSCompositePkWithIdentity.delete_all
|
|
56
|
+
SSCompositePkWithIdentity.create(pk_col_two: 2)
|
|
57
|
+
SSCompositePkWithIdentity.create(pk_col_two: 1)
|
|
58
|
+
|
|
59
|
+
_(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "orders by the first column if table has no identity column" do
|
|
63
|
+
SSCompositePkWithoutIdentity.delete_all
|
|
64
|
+
SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2)
|
|
65
|
+
SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1)
|
|
66
|
+
|
|
67
|
+
_(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
describe 'local server' do
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
|
|
6
|
+
describe "local server" do
|
|
7
|
+
it "should use table name in select projections" do
|
|
8
8
|
table = Arel::Table.new(:table)
|
|
9
9
|
expected_sql = "SELECT [table].[name] FROM [table]"
|
|
10
10
|
assert_equal expected_sql, table.project(table[:name]).to_sql
|
|
11
11
|
end
|
|
12
|
-
|
|
13
12
|
end
|
|
14
13
|
|
|
15
|
-
describe
|
|
16
|
-
|
|
14
|
+
describe "remote server" do
|
|
17
15
|
before do
|
|
18
16
|
connection_options[:database_prefix] = "[my.server].db.schema."
|
|
19
17
|
end
|
|
@@ -22,39 +20,39 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
|
|
|
22
20
|
connection_options.delete :database_prefix
|
|
23
21
|
end
|
|
24
22
|
|
|
25
|
-
it
|
|
23
|
+
it "should use fully qualified table name in select from clause" do
|
|
26
24
|
table = Arel::Table.new(:table)
|
|
27
25
|
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]"
|
|
28
26
|
assert_equal expected_sql, table.project(Arel.star).to_sql
|
|
29
27
|
end
|
|
30
28
|
|
|
31
|
-
it
|
|
29
|
+
it "should not use fully qualified table name in select projections" do
|
|
32
30
|
table = Arel::Table.new(:table)
|
|
33
31
|
expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]"
|
|
34
32
|
assert_equal expected_sql, table.project(table[:name]).to_sql
|
|
35
33
|
end
|
|
36
34
|
|
|
37
|
-
it
|
|
35
|
+
it "should not use fully qualified table name in where clause" do
|
|
38
36
|
table = Arel::Table.new(:table)
|
|
39
37
|
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
|
|
40
38
|
quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql }
|
|
41
39
|
end
|
|
42
40
|
|
|
43
|
-
it
|
|
41
|
+
it "should not use fully qualified table name in order clause" do
|
|
44
42
|
table = Arel::Table.new(:table)
|
|
45
|
-
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]
|
|
43
|
+
expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]"
|
|
46
44
|
assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
it
|
|
47
|
+
it "should use fully qualified table name in insert statement" do
|
|
50
48
|
manager = Arel::InsertManager.new
|
|
51
49
|
manager.into Arel::Table.new(:table)
|
|
52
|
-
manager.values = manager.create_values [Arel.sql(
|
|
50
|
+
manager.values = manager.create_values [Arel.sql("*")]
|
|
53
51
|
expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)"
|
|
54
52
|
quietly { assert_equal expected_sql, manager.to_sql }
|
|
55
53
|
end
|
|
56
54
|
|
|
57
|
-
it
|
|
55
|
+
it "should use fully qualified table name in update statement" do
|
|
58
56
|
table = Arel::Table.new(:table)
|
|
59
57
|
manager = Arel::UpdateManager.new
|
|
60
58
|
manager.table(table).where(table[:id].eq(42))
|
|
@@ -63,14 +61,12 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase
|
|
|
63
61
|
quietly { assert_equal expected_sql, manager.to_sql }
|
|
64
62
|
end
|
|
65
63
|
|
|
66
|
-
it
|
|
64
|
+
it "should use fully qualified table name in delete statement" do
|
|
67
65
|
table = Arel::Table.new(:table)
|
|
68
66
|
manager = Arel::DeleteManager.new
|
|
69
67
|
manager.from(table).where(table[:id].eq(42))
|
|
70
68
|
expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42"
|
|
71
69
|
quietly { assert_equal expected_sql, manager.to_sql }
|
|
72
70
|
end
|
|
73
|
-
|
|
74
71
|
end
|
|
75
|
-
|
|
76
72
|
end
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "support/paths_sqlserver"
|
|
4
|
+
require "bundler/setup"
|
|
3
5
|
Bundler.require :default, :development
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
13
|
-
require
|
|
6
|
+
require "pry"
|
|
7
|
+
require "support/core_ext/query_cache"
|
|
8
|
+
require "support/minitest_sqlserver"
|
|
9
|
+
require "support/test_in_memory_oltp"
|
|
10
|
+
require "cases/helper"
|
|
11
|
+
require "support/load_schema_sqlserver"
|
|
12
|
+
require "support/coerceable_test_sqlserver"
|
|
13
|
+
require "support/sql_counter_sqlserver"
|
|
14
|
+
require "support/connection_reflection"
|
|
15
|
+
require "mocha/minitest"
|
|
14
16
|
|
|
15
17
|
module ActiveRecord
|
|
16
18
|
class TestCase < ActiveSupport::TestCase
|
|
17
|
-
|
|
18
19
|
SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
|
|
19
20
|
|
|
20
21
|
include ARTest::SQLServer::CoerceableTest,
|
|
@@ -38,7 +39,7 @@ module ActiveRecord
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def host_windows?
|
|
41
|
-
RbConfig::CONFIG[
|
|
42
|
+
RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def with_use_output_inserted_disabled
|
|
@@ -48,7 +49,6 @@ module ActiveRecord
|
|
|
48
49
|
ensure
|
|
49
50
|
klass.use_output_inserted = true
|
|
50
51
|
end
|
|
51
|
-
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "models/post"
|
|
5
|
+
require "models/author"
|
|
6
|
+
|
|
7
|
+
class InClauseTestSQLServer < ActiveRecord::TestCase
|
|
8
|
+
fixtures :posts, :authors
|
|
9
|
+
|
|
10
|
+
it "removes ordering from subqueries" do
|
|
11
|
+
authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name)
|
|
12
|
+
posts = Post.where(author: authors_subquery)
|
|
13
|
+
|
|
14
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
15
|
+
assert_not_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
16
|
+
assert_equal 10, posts.length
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "does not remove ordering from subquery that includes a limit" do
|
|
20
|
+
authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).limit(2)
|
|
21
|
+
posts = Post.where(author: authors_subquery)
|
|
22
|
+
|
|
23
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
24
|
+
assert_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
25
|
+
assert_equal 7, posts.length
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "does not remove ordering from subquery that includes an offset" do
|
|
29
|
+
authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).offset(1)
|
|
30
|
+
posts = Post.where(author: authors_subquery)
|
|
31
|
+
|
|
32
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
33
|
+
assert_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
34
|
+
assert_equal 8, posts.length
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "removes ordering from 'not' subqueries" do
|
|
38
|
+
authors_subquery = Author.where.not(name: ["Mary", "Bob"]).order(:name)
|
|
39
|
+
posts = Post.where(author: authors_subquery)
|
|
40
|
+
|
|
41
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
42
|
+
assert_not_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
43
|
+
assert_equal 5, posts.length
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "does not remove ordering from 'not' subquery that includes a limit" do
|
|
47
|
+
authors_subquery = Author.where.not(name: ["Ronan", "Mary", "Bob"]).order(:name).limit(2)
|
|
48
|
+
posts = Post.where(author: authors_subquery)
|
|
49
|
+
|
|
50
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
51
|
+
assert_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
52
|
+
assert_equal 5, posts.length
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "does not remove ordering from 'not' subquery that includes an offset" do
|
|
56
|
+
authors_subquery = Author.where.not(name: ["David", "Ronan", "Cian"]).order(:name).offset(1)
|
|
57
|
+
posts = Post.where(author: authors_subquery)
|
|
58
|
+
|
|
59
|
+
assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]"
|
|
60
|
+
assert_includes posts.to_sql, "ORDER BY [authors].[name]"
|
|
61
|
+
assert_equal 3, posts.length
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
4
|
|
|
5
|
+
class IndexTestSQLServer < ActiveRecord::TestCase
|
|
5
6
|
before do
|
|
6
7
|
connection.create_table(:testings) do |t|
|
|
7
8
|
t.column :foo, :string, limit: 100
|
|
@@ -17,31 +18,30 @@ class IndexTestSQLServer < ActiveRecord::TestCase
|
|
|
17
18
|
connection.drop_table :testings rescue nil
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
it
|
|
21
|
+
it "add index with order" do
|
|
21
22
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do
|
|
22
|
-
connection.add_index
|
|
23
|
-
connection.remove_index
|
|
23
|
+
connection.add_index "testings", ["last_name"], order: { last_name: :desc }
|
|
24
|
+
connection.remove_index "testings", ["last_name"]
|
|
24
25
|
end
|
|
25
26
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do
|
|
26
|
-
connection.add_index
|
|
27
|
-
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"]
|
|
28
29
|
end
|
|
29
30
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do
|
|
30
|
-
connection.add_index
|
|
31
|
-
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"]
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
it
|
|
36
|
+
it "add index with where" do
|
|
36
37
|
assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do
|
|
37
|
-
connection.add_index
|
|
38
|
-
connection.remove_index
|
|
38
|
+
connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'"
|
|
39
|
+
connection.remove_index "testings", "last_name"
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
it
|
|
43
|
+
it "add index with expression" do
|
|
43
44
|
connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])"
|
|
44
|
-
connection.add_index
|
|
45
|
+
connection.add_index "testings", "first_name_upper"
|
|
45
46
|
end
|
|
46
|
-
|
|
47
47
|
end
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
2
4
|
|
|
3
5
|
if ActiveRecord::Base.connection.supports_json?
|
|
4
|
-
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
|
|
5
14
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@o1.save!
|
|
19
|
-
_(@o1.reload.json_col).must_equal({ 'a' => 'a' })
|
|
20
|
-
end
|
|
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
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
it "can use JSON_VALUE function" do
|
|
29
|
+
_(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2
|
|
30
|
+
end
|
|
25
31
|
end
|
|
26
|
-
|
|
27
|
-
it 'can use JSON_VALUE function' do
|
|
28
|
-
_(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
end
|
|
32
32
|
end
|