activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|