activerecord-jdbcsqlserver-adapter 50.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +124 -0
- data/CODE_OF_CONDUCT.md +31 -0
- data/Dockerfile +20 -0
- data/Gemfile +77 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +20 -0
- data/RAILS5-TODO.md +5 -0
- data/README.md +93 -0
- data/RUNNING_UNIT_TESTS.md +96 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/activerecord-jdbcsqlserver-adapter.gemspec +21 -0
- data/appveyor.yml +39 -0
- data/docker-compose.ci.yml +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/date_time.rb +58 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +47 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +362 -0
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +67 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
- data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +192 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +99 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +34 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +517 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +112 -0
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +64 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +61 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +71 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +29 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +68 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +93 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +146 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +445 -0
- data/lib/active_record/connection_adapters/sqlserver_column.rb +28 -0
- data/lib/active_record/jdbc_sqlserver_connection_methods.rb +31 -0
- data/lib/active_record/sqlserver_base.rb +16 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
- data/lib/activerecord-jdbcsqlserver-adapter.rb +24 -0
- data/lib/activerecord-sqlserver-adapter.rb +1 -0
- data/lib/arel/visitors/sqlserver.rb +205 -0
- data/lib/arel_sqlserver.rb +3 -0
- data/test/appveyor/dbsetup.ps1 +27 -0
- data/test/appveyor/dbsetup.sql +11 -0
- data/test/bin/wait-for.sh +79 -0
- data/test/cases/adapter_test_sqlserver.rb +430 -0
- data/test/cases/coerced_tests.rb +845 -0
- data/test/cases/column_test_sqlserver.rb +812 -0
- data/test/cases/connection_test_sqlserver.rb +71 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +45 -0
- data/test/cases/fetch_test_sqlserver.rb +57 -0
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
- data/test/cases/helper_sqlserver.rb +44 -0
- data/test/cases/index_test_sqlserver.rb +47 -0
- data/test/cases/json_test_sqlserver.rb +32 -0
- data/test/cases/migration_test_sqlserver.rb +61 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +94 -0
- data/test/cases/rake_test_sqlserver.rb +169 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +234 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +8 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +180 -0
- data/test/cases/transaction_test_sqlserver.rb +91 -0
- data/test/cases/utils_test_sqlserver.rb +129 -0
- data/test/cases/uuid_test_sqlserver.rb +49 -0
- data/test/config.yml +38 -0
- data/test/debug.rb +14 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/booking.rb +3 -0
- data/test/models/sqlserver/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +8 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -0
- data/test/models/sqlserver/dot_table_name.rb +3 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +3 -0
- data/test/models/sqlserver/fk_has_pk.rb +3 -0
- data/test/models/sqlserver/natural_pk_data.rb +4 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
- data/test/models/sqlserver/no_pk_data.rb +3 -0
- data/test/models/sqlserver/object_default.rb +3 -0
- data/test/models/sqlserver/quoted_table.rb +7 -0
- data/test/models/sqlserver/quoted_view_1.rb +3 -0
- data/test/models/sqlserver/quoted_view_2.rb +3 -0
- data/test/models/sqlserver/sst_memory.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
- data/test/models/sqlserver/string_defaults_view.rb +3 -0
- data/test/models/sqlserver/tinyint_pk.rb +3 -0
- data/test/models/sqlserver/upper.rb +3 -0
- data/test/models/sqlserver/uppered.rb +3 -0
- data/test/models/sqlserver/uuid.rb +3 -0
- data/test/schema/datatypes/2012.sql +55 -0
- data/test/schema/enable-in-memory-oltp.sql +81 -0
- data/test/schema/sqlserver_specific_schema.rb +238 -0
- data/test/support/coerceable_test_sqlserver.rb +49 -0
- data/test/support/connection_reflection.rb +34 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +50 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +28 -0
- data/test/support/test_in_memory_oltp.rb +15 -0
- metadata +310 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
class SchemaTestSQLServer < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
describe 'When table is dbo schema' do
|
6
|
+
|
7
|
+
it 'find primary key for tables with odd schema' do
|
8
|
+
connection.primary_key('sst_natural_pk_data').must_equal 'legacy_id'
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'When table is in non-dbo schema' do
|
14
|
+
|
15
|
+
it 'work with table exists' do
|
16
|
+
assert connection.data_source_exists?('test.sst_schema_natural_id')
|
17
|
+
assert connection.data_source_exists?('[test].[sst_schema_natural_id]')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'find primary key for tables with odd schema' do
|
21
|
+
connection.primary_key('test.sst_schema_natural_id').must_equal 'legacy_id'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "have only one identity column" do
|
25
|
+
columns = connection.columns("test.sst_schema_identity")
|
26
|
+
assert_equal 2, columns.size
|
27
|
+
assert_equal 1, columns.select{ |c| c.is_identity? }.size
|
28
|
+
end
|
29
|
+
|
30
|
+
it "read only column properties for table in specific schema" do
|
31
|
+
test_columns = connection.columns("test.sst_schema_columns")
|
32
|
+
dbo_columns = connection.columns("dbo.sst_schema_columns")
|
33
|
+
columns = connection.columns("sst_schema_columns") # This returns table from dbo schema
|
34
|
+
assert_equal 7, test_columns.size
|
35
|
+
assert_equal 2, dbo_columns.size
|
36
|
+
assert_equal 2, columns.size
|
37
|
+
assert_equal 1, test_columns.select{ |c| c.is_identity? }.size
|
38
|
+
assert_equal 1, dbo_columns.select{ |c| c.is_identity? }.size
|
39
|
+
assert_equal 1, columns.select{ |c| c.is_identity? }.size
|
40
|
+
end
|
41
|
+
|
42
|
+
it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do
|
43
|
+
columns = connection.columns("test.sst_schema_columns")
|
44
|
+
assert_equal 255, columns.find {|c| c.name == 'name'}.limit
|
45
|
+
assert_equal 1000, columns.find {|c| c.name == 'description'}.limit
|
46
|
+
assert_equal 255, columns.find {|c| c.name == 'n_name'}.limit
|
47
|
+
assert_equal 1000, columns.find {|c| c.name == 'n_description'}.limit
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
require 'models/car'
|
3
|
+
|
4
|
+
class ShowplanTestSQLServer < ActiveRecord::TestCase
|
5
|
+
|
6
|
+
fixtures :cars
|
7
|
+
|
8
|
+
describe 'Unprepare previously prepared SQL' do
|
9
|
+
|
10
|
+
it 'from simple statement' do
|
11
|
+
plan = Car.where(id: 1).explain
|
12
|
+
plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1"
|
13
|
+
plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'from multiline statement' do
|
17
|
+
plan = Car.where("\n id = 1 \n").explain
|
18
|
+
plan.must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)"
|
19
|
+
plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'from prepared statement' do
|
23
|
+
plan = Car.where(name: ',').limit(1).explain
|
24
|
+
plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]"
|
25
|
+
plan.must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql'
|
26
|
+
plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'With SHOWPLAN_TEXT option' do
|
32
|
+
|
33
|
+
it 'use simple table printer' do
|
34
|
+
with_showplan_option('SHOWPLAN_TEXT') do
|
35
|
+
plan = Car.where(id: 1).explain
|
36
|
+
plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]"
|
37
|
+
plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'With SHOWPLAN_XML option' do
|
44
|
+
|
45
|
+
it 'show formatted xml' do
|
46
|
+
with_showplan_option('SHOWPLAN_XML') do
|
47
|
+
plan = Car.where(id: 1).explain
|
48
|
+
plan.must_include 'ShowPlanXML'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def with_showplan_option(option)
|
58
|
+
old_option = ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option
|
59
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = option
|
60
|
+
yield
|
61
|
+
ensure
|
62
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = old_option
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
class SpecificSchemaTestSQLServer < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
after { SSTestEdgeSchema.delete_all }
|
6
|
+
|
7
|
+
it 'handle dollar symbols' do
|
8
|
+
SSTestDollarTableName.create!
|
9
|
+
SSTestDollarTableName.limit(20).offset(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'handle dot table names' do
|
13
|
+
SSTestDotTableName.create! name: 'test'
|
14
|
+
SSTestDotTableName.limit(20).offset(1)
|
15
|
+
SSTestDotTableName.where(name: 'test').first.must_be :present?
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'models can use tinyint pk tables' do
|
19
|
+
obj = SSTestTinyintPk.create! name: '1'
|
20
|
+
['Fixnum', 'Integer'].must_include obj.id.class.name
|
21
|
+
SSTestTinyintPk.find(obj.id).must_equal obj
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'be able to complex count tables with no primary key' do
|
25
|
+
SSTestNoPkData.delete_all
|
26
|
+
10.times { |n| SSTestNoPkData.create! name: "Test#{n}" }
|
27
|
+
assert_equal 1, SSTestNoPkData.where(name: 'Test5').count
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'quote table names properly even when they are views' do
|
31
|
+
obj = SSTestQuotedTable.create!
|
32
|
+
assert_nothing_raised { assert SSTestQuotedTable.first }
|
33
|
+
obj = SSTestQuotedTableUser.create!
|
34
|
+
assert_nothing_raised { assert SSTestQuotedTableUser.first }
|
35
|
+
obj = SSTestQuotedView1.create!
|
36
|
+
assert_nothing_raised { assert SSTestQuotedView1.first }
|
37
|
+
obj = SSTestQuotedView2.create!
|
38
|
+
assert_nothing_raised { assert SSTestQuotedView2.first }
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'cope with multi line defaults' do
|
42
|
+
default = SSTestStringDefault.new
|
43
|
+
assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'default strings before save' do
|
47
|
+
default = SSTestStringDefault.new
|
48
|
+
assert_nil default.string_with_null_default
|
49
|
+
assert_equal 'null', default.string_with_pretend_null_one
|
50
|
+
assert_equal '(null)', default.string_with_pretend_null_two
|
51
|
+
assert_equal 'NULL', default.string_with_pretend_null_three
|
52
|
+
assert_equal '(NULL)', default.string_with_pretend_null_four
|
53
|
+
assert_equal '(3)', default.string_with_pretend_paren_three
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'default strings after save' do
|
57
|
+
default = SSTestStringDefault.create
|
58
|
+
assert_nil default.string_with_null_default
|
59
|
+
assert_equal 'null', default.string_with_pretend_null_one
|
60
|
+
assert_equal '(null)', default.string_with_pretend_null_two
|
61
|
+
assert_equal 'NULL', default.string_with_pretend_null_three
|
62
|
+
assert_equal '(NULL)', default.string_with_pretend_null_four
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'default objects work' do
|
66
|
+
obj = SSTestObjectDefault.create! name: 'MetaSkills'
|
67
|
+
obj.date.must_be_nil 'since this is set on insert'
|
68
|
+
obj.reload.date.must_be_instance_of Date
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'allows datetime2 as timestamps' do
|
72
|
+
SSTestBooking.columns_hash['created_at'].sql_type.must_equal 'datetime2(7)'
|
73
|
+
SSTestBooking.columns_hash['updated_at'].sql_type.must_equal 'datetime2(7)'
|
74
|
+
obj1 = SSTestBooking.new name: 'test1'
|
75
|
+
obj1.save!
|
76
|
+
obj1.created_at.must_be_instance_of Time
|
77
|
+
obj1.updated_at.must_be_instance_of Time
|
78
|
+
end
|
79
|
+
|
80
|
+
# Natural primary keys.
|
81
|
+
|
82
|
+
it 'work with identity inserts' do
|
83
|
+
record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.'
|
84
|
+
record.id = '12345ABCDE'
|
85
|
+
assert record.save
|
86
|
+
assert_equal '12345ABCDE', record.reload.id
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'work with identity inserts when the key is an int' do
|
90
|
+
record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.'
|
91
|
+
record.id = 12
|
92
|
+
assert record.save
|
93
|
+
assert_equal 12, record.reload.id
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'use primary key for row table order in pagination sql' do
|
97
|
+
if defined? JRUBY_VERSION
|
98
|
+
sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET \? ROWS FETCH NEXT \? ROWS ONLY/
|
99
|
+
else
|
100
|
+
sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/
|
101
|
+
end
|
102
|
+
assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Special quoted column
|
106
|
+
|
107
|
+
it 'work as normal' do
|
108
|
+
SSTestEdgeSchema.delete_all
|
109
|
+
r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute'
|
110
|
+
assert SSTestEdgeSchema.columns_hash['crazy]]quote']
|
111
|
+
assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'various methods to bypass national quoted columns for any column, but primarily useful for char/varchar' do
|
115
|
+
value = Class.new do
|
116
|
+
def quoted_id
|
117
|
+
"'T'"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
# Using ActiveRecord's quoted_id feature for objects.
|
121
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first }
|
122
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first }
|
123
|
+
# Using our custom char type data.
|
124
|
+
type = ActiveRecord::Type::SQLServer::Char
|
125
|
+
data = ActiveRecord::Type::SQLServer::Data
|
126
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T', type.new)).first }
|
127
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T', type.new)).first }
|
128
|
+
# Taking care of everything.
|
129
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first }
|
130
|
+
assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first }
|
131
|
+
end unless defined? JRUBY_VERSION
|
132
|
+
|
133
|
+
it 'can update and hence properly quoted non-national char/varchar columns' do
|
134
|
+
o = SSTestDatatypeMigration.create!
|
135
|
+
o.varchar_col = "O'Reilly"
|
136
|
+
o.save!
|
137
|
+
o.reload.varchar_col.must_equal "O'Reilly"
|
138
|
+
o.varchar_col = nil
|
139
|
+
o.save!
|
140
|
+
o.reload.varchar_col.must_be_nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# With column names that have spaces
|
144
|
+
|
145
|
+
it 'create record using a custom attribute reader and be able to load it back in' do
|
146
|
+
value = 'Saved value into a column that has a space in the name.'
|
147
|
+
record = SSTestEdgeSchema.create! with_spaces: value
|
148
|
+
assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
|
149
|
+
end
|
150
|
+
|
151
|
+
# With description column
|
152
|
+
|
153
|
+
it 'allow all sorts of ordering without adapter munging it up with special description column' do
|
154
|
+
SSTestEdgeSchema.create! description: 'A'
|
155
|
+
SSTestEdgeSchema.create! description: 'B'
|
156
|
+
SSTestEdgeSchema.create! description: 'C'
|
157
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description)
|
158
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description)
|
159
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description)
|
160
|
+
assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description)
|
161
|
+
assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description)
|
162
|
+
end
|
163
|
+
|
164
|
+
# For uniqueidentifier model helpers
|
165
|
+
|
166
|
+
it 'returns a new id via connection newid_function' do
|
167
|
+
acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
|
168
|
+
db_uuid = ActiveRecord::Base.connection.newid_function
|
169
|
+
db_uuid.must_match(acceptable_uuid)
|
170
|
+
end
|
171
|
+
|
172
|
+
# with similar table definition in two schemas
|
173
|
+
|
174
|
+
it 'returns the correct primary columns' do
|
175
|
+
connection = ActiveRecord::Base.connection
|
176
|
+
assert_equal 'field_1', connection.columns('test.sst_schema_test_mulitple_schema').detect(&:is_primary?).name
|
177
|
+
assert_equal 'field_2', connection.columns('test2.sst_schema_test_mulitple_schema').detect(&:is_primary?).name
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'cases/helper_sqlserver'
|
3
|
+
require 'models/ship'
|
4
|
+
require 'models/developer'
|
5
|
+
|
6
|
+
class TransactionTestSQLServer < ActiveRecord::TestCase
|
7
|
+
|
8
|
+
self.use_transactional_tests = false
|
9
|
+
|
10
|
+
before { delete_ships }
|
11
|
+
|
12
|
+
it 'allow ActiveRecord::Rollback to work in 1 transaction block' do
|
13
|
+
Ship.transaction do
|
14
|
+
Ship.create! name: 'Black Pearl'
|
15
|
+
raise ActiveRecord::Rollback
|
16
|
+
end
|
17
|
+
assert_no_ships
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allow nested transactions to totally rollback' do
|
21
|
+
begin
|
22
|
+
Ship.transaction do
|
23
|
+
Ship.create! name: 'Black Pearl'
|
24
|
+
Ship.transaction do
|
25
|
+
Ship.create! name: 'Flying Dutchman'
|
26
|
+
raise 'HELL'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rescue Exception => e
|
30
|
+
assert_no_ships
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can use an isolation level and reverts back to starting isolation level' do
|
35
|
+
in_level = nil
|
36
|
+
begin_level = connection.user_options_isolation_level
|
37
|
+
begin_level.must_match %r{read committed}i
|
38
|
+
Ship.transaction(isolation: :serializable) do
|
39
|
+
Ship.create! name: 'Black Pearl'
|
40
|
+
in_level = connection.user_options_isolation_level
|
41
|
+
end
|
42
|
+
after_level = connection.user_options_isolation_level
|
43
|
+
in_level.must_match %r{serializable}i
|
44
|
+
after_level.must_match %r{read committed}i
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can use an isolation level and reverts back to starting isolation level under exceptions' do
|
48
|
+
connection.user_options_isolation_level.must_match %r{read committed}i
|
49
|
+
lambda {
|
50
|
+
Ship.transaction(isolation: :serializable) { Ship.create! }
|
51
|
+
}.must_raise(ActiveRecord::RecordInvalid)
|
52
|
+
connection.user_options_isolation_level.must_match %r{read committed}i
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'when READ_COMMITTED_SNAPSHOT is set' do
|
56
|
+
before do
|
57
|
+
connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON"
|
58
|
+
connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE"
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF"
|
63
|
+
connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE"
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should use READ COMMITTED as an isolation level' do
|
67
|
+
connection.user_options_isolation_level.must_match "read committed snapshot"
|
68
|
+
|
69
|
+
Ship.transaction(isolation: :serializable) do
|
70
|
+
Ship.create! name: 'Black Pearl'
|
71
|
+
end
|
72
|
+
|
73
|
+
# We're actually testing that the isolation level was correctly reset to
|
74
|
+
# "READ COMMITTED", and that no exception was raised (it's reported back
|
75
|
+
# by SQL Server as "read committed snapshot").
|
76
|
+
connection.user_options_isolation_level.must_match "read committed snapshot"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def delete_ships
|
84
|
+
Ship.delete_all
|
85
|
+
end
|
86
|
+
|
87
|
+
def assert_no_ships
|
88
|
+
assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}"
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'cases/helper_sqlserver'
|
2
|
+
|
3
|
+
class UtilsTestSQLServer < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
it '.quote_string' do
|
6
|
+
SQLServer::Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users"
|
7
|
+
end
|
8
|
+
|
9
|
+
it '.unquote_string' do
|
10
|
+
SQLServer::Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users"
|
11
|
+
end
|
12
|
+
|
13
|
+
it '.quoted_raw' do
|
14
|
+
SQLServer::Utils.quoted_raw("some.Name").must_equal "[some.Name]"
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do
|
18
|
+
|
19
|
+
let(:valid_names) { valid_names_unquoted + valid_names_quoted }
|
20
|
+
|
21
|
+
let(:valid_names_unquoted) {[
|
22
|
+
'server.database.schema.object',
|
23
|
+
'server.database..object',
|
24
|
+
'server..schema.object',
|
25
|
+
'server...object',
|
26
|
+
'database.schema.object',
|
27
|
+
'database..object',
|
28
|
+
'schema.object',
|
29
|
+
'object'
|
30
|
+
]}
|
31
|
+
|
32
|
+
let(:valid_names_quoted) {[
|
33
|
+
'[server].[database].[schema].[object]',
|
34
|
+
'[server].[database]..[object]',
|
35
|
+
'[server]..[schema].[object]',
|
36
|
+
'[server]...[object]',
|
37
|
+
'[database].[schema].[object]',
|
38
|
+
'[database]..[object]',
|
39
|
+
'[schema].[object]',
|
40
|
+
'[object]'
|
41
|
+
]}
|
42
|
+
|
43
|
+
let(:server_names) { valid_names.partition { |name| name =~ /server/ } }
|
44
|
+
let(:database_names) { valid_names.partition { |name| name =~ /database/ } }
|
45
|
+
let(:schema_names) { valid_names.partition { |name| name =~ /schema/ } }
|
46
|
+
|
47
|
+
it 'extracts and returns #object identifier unquoted by default or quoted as needed' do
|
48
|
+
valid_names.each do |n|
|
49
|
+
name = extract_identifiers(n)
|
50
|
+
name.object.must_equal 'object', "With #{n.inspect} for #object"
|
51
|
+
name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
[:schema, :database, :server].each do |part|
|
56
|
+
|
57
|
+
it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do
|
58
|
+
present, blank = send(:"#{part}_names")
|
59
|
+
present.each do |n|
|
60
|
+
name = extract_identifiers(n)
|
61
|
+
name.send(:"#{part}").must_equal "#{part}", "With #{n.inspect} for ##{part} method"
|
62
|
+
name.send(:"#{part}_quoted").must_equal "[#{part}]", "With #{n.inspect} for ##{part}_quoted method"
|
63
|
+
end
|
64
|
+
blank.each do |n|
|
65
|
+
name = extract_identifiers(n)
|
66
|
+
name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method"
|
67
|
+
name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does not blow up on nil or blank string name' do
|
74
|
+
extract_identifiers(nil).object.must_be_nil
|
75
|
+
extract_identifiers(' ').object.must_be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do
|
79
|
+
extract_identifiers('object').quoted.must_equal '[object]'
|
80
|
+
extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]'
|
81
|
+
extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'can take a symbol argument' do
|
85
|
+
extract_identifiers(:object).object.must_equal 'object'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'allows identifiers with periods to work' do
|
89
|
+
extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]'
|
90
|
+
extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should indicate if a name is fully qualitified' do
|
94
|
+
extract_identifiers('object').fully_qualified?.must_equal false
|
95
|
+
extract_identifiers('schema.object').fully_qualified?.must_equal false
|
96
|
+
extract_identifiers('database.schema.object').fully_qualified?.must_equal false
|
97
|
+
extract_identifiers('database.object').fully_qualified?.must_equal false
|
98
|
+
extract_identifiers('server...object').fully_qualified?.must_equal false
|
99
|
+
extract_identifiers('server.database..object').fully_qualified?.must_equal false
|
100
|
+
extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true
|
101
|
+
extract_identifiers('server.database.schema.').fully_qualified?.must_equal true
|
102
|
+
extract_identifiers('[obj.name]').fully_qualified?.must_equal false
|
103
|
+
extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false
|
104
|
+
extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false
|
105
|
+
extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false
|
106
|
+
extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false
|
107
|
+
extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false
|
108
|
+
extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true
|
109
|
+
extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'can return fully qualified quoted table name' do
|
113
|
+
name = extract_identifiers('[my.server].db.schema.')
|
114
|
+
name.fully_qualified_database_quoted.must_equal '[my.server].[db]'
|
115
|
+
name = extract_identifiers('[server.name].[database].[schema].[object]')
|
116
|
+
name.fully_qualified_database_quoted.must_equal '[server.name].[database]'
|
117
|
+
name = extract_identifiers('server.database.schema.object')
|
118
|
+
name.fully_qualified_database_quoted.must_equal '[server].[database]'
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def extract_identifiers(name)
|
126
|
+
SQLServer::Utils.extract_identifiers(name)
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|