activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre
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/.gitignore +15 -0
- data/CHANGELOG.md +60 -0
- data/Gemfile +45 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +5 -5
- data/README.md +193 -0
- data/RUNNING_UNIT_TESTS.md +95 -0
- data/Rakefile +48 -0
- data/activerecord-sqlserver-adapter.gemspec +28 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +5 -15
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +130 -151
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -25
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +39 -78
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +71 -47
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -30
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +112 -108
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +52 -7
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +52 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/castable.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +39 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +14 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +37 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +59 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -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 +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +118 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +133 -198
- data/lib/active_record/connection_adapters/sqlserver_column.rb +15 -86
- data/lib/active_record/sqlserver_base.rb +2 -0
- data/lib/arel/visitors/sqlserver.rb +120 -393
- data/lib/{arel/arel_sqlserver.rb → arel_sqlserver.rb} +1 -3
- data/test/cases/adapter_test_sqlserver.rb +420 -0
- data/test/cases/coerced_tests.rb +642 -0
- data/test/cases/column_test_sqlserver.rb +703 -0
- data/test/cases/connection_test_sqlserver.rb +216 -0
- data/test/cases/database_statements_test_sqlserver.rb +57 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +38 -0
- data/test/cases/helper_sqlserver.rb +36 -0
- data/test/cases/migration_test_sqlserver.rb +66 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +175 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +9 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +118 -0
- data/test/cases/transaction_test_sqlserver.rb +61 -0
- data/test/cases/utils_test_sqlserver.rb +91 -0
- data/test/cases/uuid_test_sqlserver.rb +41 -0
- data/test/config.yml +35 -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/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +3 -0
- data/test/models/sqlserver/dollar_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/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/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 +64 -0
- data/test/schema/sqlserver_specific_schema.rb +181 -0
- data/test/support/coerceable_test_sqlserver.rb +45 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +48 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +32 -0
- metadata +271 -21
- data/CHANGELOG +0 -39
- data/VERSION +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +0 -17
- data/lib/active_record/sqlserver_test_case.rb +0 -17
- data/lib/arel/nodes_sqlserver.rb +0 -14
- data/lib/arel/select_manager_sqlserver.rb +0 -62
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
require 'models/person'
|
|
3
|
+
require 'models/reader'
|
|
4
|
+
|
|
5
|
+
class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
|
|
6
|
+
|
|
7
|
+
fixtures :people, :readers
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
Person.columns
|
|
11
|
+
Reader.columns
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'uses with updlock by default' do
|
|
15
|
+
assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do
|
|
16
|
+
Person.lock(true).to_a.must_equal Person.all.to_a
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe 'For simple finds with default lock option' do
|
|
21
|
+
|
|
22
|
+
it 'lock with simple find' do
|
|
23
|
+
assert_nothing_raised do
|
|
24
|
+
Person.transaction do
|
|
25
|
+
Person.lock(true).find(1).must_equal Person.find(1)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'lock with scoped find' do
|
|
31
|
+
assert_nothing_raised do
|
|
32
|
+
Person.transaction do
|
|
33
|
+
Person.lock(true).scoping do
|
|
34
|
+
Person.find(1).must_equal Person.find(1)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'lock with eager find' do
|
|
41
|
+
assert_nothing_raised do
|
|
42
|
+
Person.transaction do
|
|
43
|
+
person = Person.lock(true).includes(:readers).find(1)
|
|
44
|
+
person.must_equal Person.find(1)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'reload with lock when #lock! called' do
|
|
50
|
+
assert_nothing_raised do
|
|
51
|
+
Person.transaction do
|
|
52
|
+
person = Person.find 1
|
|
53
|
+
old, person.first_name = person.first_name, 'fooman'
|
|
54
|
+
person.lock!
|
|
55
|
+
assert_equal old, person.first_name
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'can add a custom lock directive' do
|
|
61
|
+
assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do
|
|
62
|
+
Person.lock('WITH(HOLDLOCK, ROWLOCK)').load
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe 'For paginated finds' do
|
|
69
|
+
|
|
70
|
+
before do
|
|
71
|
+
Person.delete_all
|
|
72
|
+
20.times { |n| Person.create!(first_name: "Thing_#{n}") }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'copes with eager loading un-locked paginated' do
|
|
76
|
+
eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/
|
|
77
|
+
loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/
|
|
78
|
+
assert_sql(eager_ids_sql, loader_sql) do
|
|
79
|
+
people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a
|
|
80
|
+
people[0].first_name.must_equal 'Thing_10'
|
|
81
|
+
people[1].first_name.must_equal 'Thing_11'
|
|
82
|
+
people[2].first_name.must_equal 'Thing_12'
|
|
83
|
+
people[3].first_name.must_equal 'Thing_13'
|
|
84
|
+
people[4].first_name.must_equal 'Thing_14'
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
|
|
3
|
+
class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
4
|
+
|
|
5
|
+
before { all_tables }
|
|
6
|
+
|
|
7
|
+
let(:all_tables) { ActiveRecord::Base.connection.tables }
|
|
8
|
+
let(:schema) { @generated_schema }
|
|
9
|
+
|
|
10
|
+
it 'sst_datatypes' do
|
|
11
|
+
generate_schema_for_table 'sst_datatypes'
|
|
12
|
+
# Exact Numerics
|
|
13
|
+
assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42'
|
|
14
|
+
assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42'
|
|
15
|
+
assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42'
|
|
16
|
+
assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42'
|
|
17
|
+
assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true'
|
|
18
|
+
assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01'
|
|
19
|
+
assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0'
|
|
20
|
+
assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01'
|
|
21
|
+
assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2'
|
|
22
|
+
assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2'
|
|
23
|
+
# Approximate Numerics
|
|
24
|
+
assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001'
|
|
25
|
+
assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]}
|
|
26
|
+
# Date and Time
|
|
27
|
+
assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'"
|
|
28
|
+
assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'"
|
|
29
|
+
assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'"
|
|
30
|
+
assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil
|
|
31
|
+
assert_line :time_7, type: 'time', limit: nil, precision: nil, scale: nil, default: nil
|
|
32
|
+
# Character Strings
|
|
33
|
+
assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\""
|
|
34
|
+
assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\""
|
|
35
|
+
assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\""
|
|
36
|
+
assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\""
|
|
37
|
+
# Unicode Character Strings
|
|
38
|
+
assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\""
|
|
39
|
+
assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\""
|
|
40
|
+
assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\""
|
|
41
|
+
assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\""
|
|
42
|
+
# Binary Strings
|
|
43
|
+
assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil
|
|
44
|
+
assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil
|
|
45
|
+
assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
|
|
46
|
+
# Other Data Types
|
|
47
|
+
assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
|
|
48
|
+
assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'sst_datatypes_migration' do
|
|
52
|
+
columns = SSTestDatatypeMigration.columns_hash
|
|
53
|
+
generate_schema_for_table 'sst_datatypes_migration'
|
|
54
|
+
# Simple Rails conventions
|
|
55
|
+
columns['integer_col'].sql_type.must_equal 'int(4)'
|
|
56
|
+
columns['bigint_col'].sql_type.must_equal 'bigint(8)'
|
|
57
|
+
columns['boolean_col'].sql_type.must_equal 'bit'
|
|
58
|
+
columns['decimal_col'].sql_type.must_equal 'decimal(18,0)'
|
|
59
|
+
columns['float_col'].sql_type.must_equal 'float'
|
|
60
|
+
columns['string_col'].sql_type.must_equal 'nvarchar(4000)'
|
|
61
|
+
columns['text_col'].sql_type.must_equal 'nvarchar(max)'
|
|
62
|
+
columns['datetime_col'].sql_type.must_equal 'datetime'
|
|
63
|
+
columns['timestamp_col'].sql_type.must_equal 'datetime'
|
|
64
|
+
columns['time_col'].sql_type.must_equal 'time(7)'
|
|
65
|
+
columns['date_col'].sql_type.must_equal 'date'
|
|
66
|
+
columns['binary_col'].sql_type.must_equal 'varbinary(max)'
|
|
67
|
+
assert_line :integer_col, type: 'integer', limit: '4', precision: nil, scale: nil, default: nil
|
|
68
|
+
assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil
|
|
69
|
+
assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil
|
|
70
|
+
assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil
|
|
71
|
+
assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil
|
|
72
|
+
assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil
|
|
73
|
+
assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil
|
|
74
|
+
assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
|
|
75
|
+
assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
|
|
76
|
+
assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil
|
|
77
|
+
assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil
|
|
78
|
+
assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
|
|
79
|
+
# Our type methods.
|
|
80
|
+
columns['real_col'].sql_type.must_equal 'real'
|
|
81
|
+
columns['money_col'].sql_type.must_equal 'money'
|
|
82
|
+
columns['smallmoney_col'].sql_type.must_equal 'smallmoney'
|
|
83
|
+
columns['char_col'].sql_type.must_equal 'char(1)'
|
|
84
|
+
columns['varchar_col'].sql_type.must_equal 'varchar(8000)'
|
|
85
|
+
columns['text_basic_col'].sql_type.must_equal 'text'
|
|
86
|
+
columns['nchar_col'].sql_type.must_equal 'nchar(1)'
|
|
87
|
+
columns['ntext_col'].sql_type.must_equal 'ntext'
|
|
88
|
+
columns['binary_basic_col'].sql_type.must_equal 'binary(1)'
|
|
89
|
+
columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)'
|
|
90
|
+
columns['uuid_col'].sql_type.must_equal 'uniqueidentifier'
|
|
91
|
+
columns['sstimestamp_col'].sql_type.must_equal 'timestamp'
|
|
92
|
+
assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil
|
|
93
|
+
assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil
|
|
94
|
+
assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil
|
|
95
|
+
assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil
|
|
96
|
+
assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil
|
|
97
|
+
assert_line :text_basic_col, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: nil
|
|
98
|
+
assert_line :nchar_col, type: 'nchar', limit: '1', precision: nil, scale: nil, default: nil
|
|
99
|
+
assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil
|
|
100
|
+
assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil
|
|
101
|
+
assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil
|
|
102
|
+
assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
|
|
103
|
+
assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Special Cases
|
|
107
|
+
|
|
108
|
+
it 'primary_key' do
|
|
109
|
+
generate_schema_for_table('movies') do |output|
|
|
110
|
+
match = output.match(%r{create_table "movies"(.*)do})
|
|
111
|
+
assert_not_nil(match, "nonstandardpk table not found")
|
|
112
|
+
assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def generate_schema_for_table(*table_names)
|
|
120
|
+
require 'stringio'
|
|
121
|
+
stream = StringIO.new
|
|
122
|
+
ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
|
|
123
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
|
124
|
+
@generated_schema = stream.string
|
|
125
|
+
yield @generated_schema if block_given?
|
|
126
|
+
@schema_lines = Hash.new
|
|
127
|
+
type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/
|
|
128
|
+
@generated_schema.each_line do |line|
|
|
129
|
+
next unless line =~ type_matcher
|
|
130
|
+
@schema_lines[Regexp.last_match[1]] = SchemaLine.new(line)
|
|
131
|
+
end
|
|
132
|
+
@generated_schema
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def line(column_name)
|
|
136
|
+
@schema_lines[column_name.to_s]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def assert_line(column_name, options={})
|
|
140
|
+
line = line(column_name)
|
|
141
|
+
assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
|
|
142
|
+
line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type)
|
|
143
|
+
line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit)
|
|
144
|
+
line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision)
|
|
145
|
+
line.scale.must_equal options[:scale], "Scale of #{options[:scale].inspect} not found in:\n #{line}" if options.key?(:scale)
|
|
146
|
+
line.default.must_equal options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(String)
|
|
147
|
+
line.default.must_match options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(Regexp)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class SchemaLine
|
|
151
|
+
|
|
152
|
+
attr_reader :line
|
|
153
|
+
|
|
154
|
+
def self.match(method_name, pattern)
|
|
155
|
+
define_method(method_name) { line.match(pattern).try :[], 1 }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def initialize(line)
|
|
159
|
+
@line = line
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
match :type_method, %r{\A\s+t\.(.*?)\s}
|
|
163
|
+
match :limit, %r{\slimit:\s(.*?)[,\s]}
|
|
164
|
+
match :default, %r{\sdefault:\s(.*)\n}
|
|
165
|
+
match :precision, %r{\sprecision:\s(.*?)[,\s]}
|
|
166
|
+
match :scale, %r{\sscale:\s(.*?)[,\s]}
|
|
167
|
+
|
|
168
|
+
def to_s
|
|
169
|
+
line
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
|
|
@@ -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.table_exists?('test.sst_schema_natural_id')
|
|
17
|
+
assert connection.table_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,118 @@
|
|
|
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.new.save
|
|
9
|
+
SSTestDollarTableName.limit(20).offset(1)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'models can use tinyint pk tables' do
|
|
13
|
+
obj = SSTestTinyintPk.create! name: '1'
|
|
14
|
+
obj.id.is_a? Fixnum
|
|
15
|
+
SSTestTinyintPk.find(obj.id).must_equal obj
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'be able to complex count tables with no primary key' do
|
|
19
|
+
SSTestNoPkData.delete_all
|
|
20
|
+
10.times { |n| SSTestNoPkData.create! name: "Test#{n}" }
|
|
21
|
+
assert_equal 1, SSTestNoPkData.where(name: 'Test5').count
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'quote table names properly even when they are views' do
|
|
25
|
+
obj = SSTestQuotedTable.create!
|
|
26
|
+
assert_nothing_raised { assert SSTestQuotedTable.first }
|
|
27
|
+
obj = SSTestQuotedTableUser.create!
|
|
28
|
+
assert_nothing_raised { assert SSTestQuotedTableUser.first }
|
|
29
|
+
obj = SSTestQuotedView1.create!
|
|
30
|
+
assert_nothing_raised { assert SSTestQuotedView1.first }
|
|
31
|
+
obj = SSTestQuotedView2.create!
|
|
32
|
+
assert_nothing_raised { assert SSTestQuotedView2.first }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'cope with multi line defaults' do
|
|
36
|
+
default = SSTestStringDefault.new
|
|
37
|
+
assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'default strings before save' do
|
|
41
|
+
default = SSTestStringDefault.new
|
|
42
|
+
assert_equal nil, default.string_with_null_default
|
|
43
|
+
assert_equal 'null', default.string_with_pretend_null_one
|
|
44
|
+
assert_equal '(null)', default.string_with_pretend_null_two
|
|
45
|
+
assert_equal 'NULL', default.string_with_pretend_null_three
|
|
46
|
+
assert_equal '(NULL)', default.string_with_pretend_null_four
|
|
47
|
+
assert_equal '(3)', default.string_with_pretend_paren_three
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'default strings after save' do
|
|
51
|
+
default = SSTestStringDefault.create
|
|
52
|
+
assert_equal nil, default.string_with_null_default
|
|
53
|
+
assert_equal 'null', default.string_with_pretend_null_one
|
|
54
|
+
assert_equal '(null)', default.string_with_pretend_null_two
|
|
55
|
+
assert_equal 'NULL', default.string_with_pretend_null_three
|
|
56
|
+
assert_equal '(NULL)', default.string_with_pretend_null_four
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Natural primary keys.
|
|
60
|
+
|
|
61
|
+
it 'work with identity inserts' do
|
|
62
|
+
record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.'
|
|
63
|
+
record.id = '12345ABCDE'
|
|
64
|
+
assert record.save
|
|
65
|
+
assert_equal '12345ABCDE', record.reload.id
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'work with identity inserts when the key is an int' do
|
|
69
|
+
record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.'
|
|
70
|
+
record.id = 12
|
|
71
|
+
assert record.save
|
|
72
|
+
assert_equal 12, record.reload.id
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'use primary key for row table order in pagination sql' do
|
|
76
|
+
sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY/
|
|
77
|
+
assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Special quoted column
|
|
81
|
+
|
|
82
|
+
it 'work as normal' do
|
|
83
|
+
SSTestEdgeSchema.delete_all
|
|
84
|
+
r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute'
|
|
85
|
+
assert SSTestEdgeSchema.columns_hash['crazy]]quote']
|
|
86
|
+
assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# With column names that have spaces
|
|
90
|
+
|
|
91
|
+
it 'create record using a custom attribute reader and be able to load it back in' do
|
|
92
|
+
value = 'Saved value into a column that has a space in the name.'
|
|
93
|
+
record = SSTestEdgeSchema.create! with_spaces: value
|
|
94
|
+
assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# With description column
|
|
98
|
+
|
|
99
|
+
it 'allow all sorts of ordering without adapter munging it up with special description column' do
|
|
100
|
+
SSTestEdgeSchema.create! description: 'A'
|
|
101
|
+
SSTestEdgeSchema.create! description: 'B'
|
|
102
|
+
SSTestEdgeSchema.create! description: 'C'
|
|
103
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description)
|
|
104
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description)
|
|
105
|
+
assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description)
|
|
106
|
+
assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description)
|
|
107
|
+
assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# For uniqueidentifier model helpers
|
|
111
|
+
|
|
112
|
+
it 'returns a new id via connection newid_function' do
|
|
113
|
+
acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
|
|
114
|
+
db_uuid = ActiveRecord::Base.connection.newid_function
|
|
115
|
+
db_uuid.must_match(acceptable_uuid)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|