activerecord-sqlserver-adapter 2.3.24 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +5 -108
- data/MIT-LICENSE +1 -1
- data/README.rdoc +33 -61
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +57 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +57 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +336 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +33 -0
- data/lib/active_record/connection_adapters/sqlserver/query_cache.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +61 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +373 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +131 -1121
- data/lib/arel/engines/sql/compilers/sqlserver_compiler.rb +267 -0
- metadata +26 -76
- data/RUNNING_UNIT_TESTS +0 -31
- data/Rakefile +0 -60
- data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/active_record.rb +0 -151
- data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/odbc.rb +0 -40
- data/test/cases/aaaa_create_tables_test_sqlserver.rb +0 -19
- data/test/cases/adapter_test_sqlserver.rb +0 -755
- data/test/cases/attribute_methods_test_sqlserver.rb +0 -33
- data/test/cases/basics_test_sqlserver.rb +0 -86
- data/test/cases/calculations_test_sqlserver.rb +0 -20
- data/test/cases/column_test_sqlserver.rb +0 -354
- data/test/cases/connection_test_sqlserver.rb +0 -148
- data/test/cases/eager_association_test_sqlserver.rb +0 -42
- data/test/cases/execute_procedure_test_sqlserver.rb +0 -35
- data/test/cases/inheritance_test_sqlserver.rb +0 -28
- data/test/cases/method_scoping_test_sqlserver.rb +0 -28
- data/test/cases/migration_test_sqlserver.rb +0 -108
- data/test/cases/named_scope_test_sqlserver.rb +0 -21
- data/test/cases/offset_and_limit_test_sqlserver.rb +0 -108
- data/test/cases/pessimistic_locking_test_sqlserver.rb +0 -125
- data/test/cases/query_cache_test_sqlserver.rb +0 -24
- data/test/cases/schema_dumper_test_sqlserver.rb +0 -72
- data/test/cases/specific_schema_test_sqlserver.rb +0 -154
- data/test/cases/sqlserver_helper.rb +0 -140
- data/test/cases/table_name_test_sqlserver.rb +0 -38
- data/test/cases/transaction_test_sqlserver.rb +0 -93
- data/test/cases/unicode_test_sqlserver.rb +0 -54
- data/test/cases/validations_test_sqlserver.rb +0 -18
- data/test/connections/native_sqlserver/connection.rb +0 -26
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -28
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +0 -11
- data/test/schema/sqlserver_specific_schema.rb +0 -113
@@ -1,151 +0,0 @@
|
|
1
|
-
require 'active_record/version'
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
module ConnectionAdapters
|
5
|
-
module SQLServerCoreExtensions
|
6
|
-
|
7
|
-
|
8
|
-
module ActiveRecord
|
9
|
-
|
10
|
-
def self.included(klass)
|
11
|
-
klass.extend ClassMethods
|
12
|
-
class << klass
|
13
|
-
alias_method_chain :reset_column_information, :sqlserver_cache_support
|
14
|
-
alias_method_chain :add_order!, :sqlserver_unique_checking
|
15
|
-
alias_method_chain :add_limit!, :sqlserver_order_checking
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
|
21
|
-
def execute_procedure(proc_name, *variables)
|
22
|
-
if connection.respond_to?(:execute_procedure)
|
23
|
-
connection.execute_procedure(proc_name,*variables)
|
24
|
-
else
|
25
|
-
[]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def coerce_sqlserver_date(*attributes)
|
30
|
-
write_inheritable_attribute :coerced_sqlserver_date_columns, Set.new(attributes.map(&:to_s))
|
31
|
-
end
|
32
|
-
|
33
|
-
def coerce_sqlserver_time(*attributes)
|
34
|
-
write_inheritable_attribute :coerced_sqlserver_time_columns, Set.new(attributes.map(&:to_s))
|
35
|
-
end
|
36
|
-
|
37
|
-
def coerced_sqlserver_date_columns
|
38
|
-
read_inheritable_attribute(:coerced_sqlserver_date_columns) || []
|
39
|
-
end
|
40
|
-
|
41
|
-
def coerced_sqlserver_time_columns
|
42
|
-
read_inheritable_attribute(:coerced_sqlserver_time_columns) || []
|
43
|
-
end
|
44
|
-
|
45
|
-
def reset_column_information_with_sqlserver_cache_support
|
46
|
-
connection.send(:initialize_sqlserver_caches) if connection.respond_to?(:sqlserver?)
|
47
|
-
reset_column_information_without_sqlserver_cache_support
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def add_limit_with_sqlserver_order_checking!(sql, options, scope = :auto)
|
53
|
-
if connection.respond_to?(:sqlserver?)
|
54
|
-
scope = scope(:find) if :auto == scope
|
55
|
-
if scope
|
56
|
-
options = options.dup
|
57
|
-
scoped_order = scope[:order]
|
58
|
-
order = options[:order]
|
59
|
-
if order && scoped_order
|
60
|
-
options[:order] = add_order_with_sqlserver_unique_checking!('', order, scope).gsub(/^ ORDER BY /,'')
|
61
|
-
elsif scoped_order
|
62
|
-
options[:order] = scoped_order
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
add_limit_without_sqlserver_order_checking!(sql, options, scope)
|
67
|
-
end
|
68
|
-
|
69
|
-
def add_order_with_sqlserver_unique_checking!(sql, order, scope = :auto)
|
70
|
-
if connection.respond_to?(:sqlserver?)
|
71
|
-
order_sql = ''
|
72
|
-
add_order_without_sqlserver_unique_checking!(order_sql, order, scope)
|
73
|
-
unless order_sql.blank?
|
74
|
-
unique_order_hash = {}
|
75
|
-
select_table_name = connection.send(:get_table_name,sql)
|
76
|
-
select_table_name.tr!('[]','') if select_table_name
|
77
|
-
orders_and_dirs_set = connection.send(:orders_and_dirs_set,order_sql)
|
78
|
-
unique_order_sql = orders_and_dirs_set.inject([]) do |array,order_dir|
|
79
|
-
ord, dir = order_dir
|
80
|
-
ord_tn_and_cn = ord.to_s.split('.').map{|o|o.tr('[]','')}
|
81
|
-
ord_table_name, ord_column_name = if ord_tn_and_cn.size > 1
|
82
|
-
ord_tn_and_cn
|
83
|
-
else
|
84
|
-
[nil, ord_tn_and_cn.first]
|
85
|
-
end
|
86
|
-
unique_key = [(ord_table_name || select_table_name), ord_column_name]
|
87
|
-
if unique_order_hash[unique_key]
|
88
|
-
array
|
89
|
-
else
|
90
|
-
unique_order_hash[unique_key] = true
|
91
|
-
array << "#{ord} #{dir}".strip
|
92
|
-
end
|
93
|
-
end.join(', ')
|
94
|
-
sql << " ORDER BY #{unique_order_sql}"
|
95
|
-
end
|
96
|
-
else
|
97
|
-
add_order_without_sqlserver_unique_checking!(sql, order, scope)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
module JoinAssociationChanges
|
104
|
-
|
105
|
-
def self.included(klass)
|
106
|
-
klass.class_eval do
|
107
|
-
include InstanceMethods
|
108
|
-
alias_method_chain :aliased_table_name_for, :sqlserver_support
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
module InstanceMethods
|
113
|
-
|
114
|
-
protected
|
115
|
-
|
116
|
-
# An exact copy, except this method has a Regexp escape on the quoted table name.
|
117
|
-
def aliased_table_name_for_with_sqlserver_support(name,suffix=nil)
|
118
|
-
if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{Regexp.escape(active_record.connection.quote_table_name(name.downcase))}\son}i
|
119
|
-
@join_dependency.table_aliases[name] += 1
|
120
|
-
end
|
121
|
-
unless @join_dependency.table_aliases[name].zero?
|
122
|
-
# if the table name has been used, then use an alias
|
123
|
-
name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
|
124
|
-
table_index = @join_dependency.table_aliases[name]
|
125
|
-
@join_dependency.table_aliases[name] += 1
|
126
|
-
name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
|
127
|
-
else
|
128
|
-
@join_dependency.table_aliases[name] += 1
|
129
|
-
end
|
130
|
-
name
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
|
145
|
-
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ActiveRecord
|
146
|
-
|
147
|
-
if ActiveRecord::VERSION::MAJOR == 2 && ActiveRecord::VERSION::MINOR >= 3
|
148
|
-
require 'active_record/associations'
|
149
|
-
ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ActiveRecord::JoinAssociationChanges
|
150
|
-
end
|
151
|
-
|
@@ -1,40 +0,0 @@
|
|
1
|
-
|
2
|
-
module ActiveRecord
|
3
|
-
module ConnectionAdapters
|
4
|
-
module SQLServerCoreExtensions
|
5
|
-
module ODBC
|
6
|
-
|
7
|
-
module TimeStamp
|
8
|
-
def to_sqlserver_string
|
9
|
-
date, time, nanoseconds = to_s.split(' ')
|
10
|
-
"#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module Statement
|
15
|
-
def finished?
|
16
|
-
begin
|
17
|
-
connected?
|
18
|
-
false
|
19
|
-
rescue ::ODBC::Error => e
|
20
|
-
true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Database
|
26
|
-
def run_block(*args)
|
27
|
-
yield sth = run(*args)
|
28
|
-
sth.drop
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
ODBC::TimeStamp.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::TimeStamp if defined?(ODBC::TimeStamp)
|
38
|
-
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::Statement if defined?(ODBC::Statement)
|
39
|
-
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::Database if defined?(ODBC::Database)
|
40
|
-
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# The filename begins with "aaaa" to ensure this is the first test.
|
2
|
-
require 'cases/sqlserver_helper'
|
3
|
-
|
4
|
-
class AAAACreateTablesTestSqlserver < ActiveRecord::TestCase
|
5
|
-
self.use_transactional_fixtures = false
|
6
|
-
|
7
|
-
should 'load activerecord schema' do
|
8
|
-
schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb"
|
9
|
-
eval(File.read(schema_file))
|
10
|
-
assert true
|
11
|
-
end
|
12
|
-
|
13
|
-
should 'load sqlserver specific schema' do
|
14
|
-
sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb"
|
15
|
-
eval(File.read(sqlserver_specific_schema_file))
|
16
|
-
assert true
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
@@ -1,755 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/task'
|
3
|
-
require 'models/reply'
|
4
|
-
require 'models/joke'
|
5
|
-
require 'models/subscriber'
|
6
|
-
|
7
|
-
class AdapterTestSqlserver < ActiveRecord::TestCase
|
8
|
-
|
9
|
-
fixtures :tasks
|
10
|
-
|
11
|
-
def setup
|
12
|
-
@connection = ActiveRecord::Base.connection
|
13
|
-
@basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')"
|
14
|
-
@basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2"
|
15
|
-
@basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)"
|
16
|
-
end
|
17
|
-
|
18
|
-
context 'For abstract behavior' do
|
19
|
-
|
20
|
-
should 'have a 128 max #table_alias_length' do
|
21
|
-
assert @connection.table_alias_length <= 128
|
22
|
-
end
|
23
|
-
|
24
|
-
should 'raise invalid statement error' do
|
25
|
-
assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") }
|
26
|
-
end
|
27
|
-
|
28
|
-
should 'be our adapter_name' do
|
29
|
-
assert_equal 'SQLServer', @connection.adapter_name
|
30
|
-
end
|
31
|
-
|
32
|
-
should 'include version in inspect' do
|
33
|
-
assert_match(/version\: \d.\d/,@connection.inspect)
|
34
|
-
end
|
35
|
-
|
36
|
-
should 'support migrations' do
|
37
|
-
assert @connection.supports_migrations?
|
38
|
-
end
|
39
|
-
|
40
|
-
should 'support DDL in transactions' do
|
41
|
-
assert @connection.supports_ddl_transactions?
|
42
|
-
end
|
43
|
-
|
44
|
-
should 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do
|
45
|
-
begin
|
46
|
-
assert_equal 'tasks', Task.table_name
|
47
|
-
assert Task.table_exists?
|
48
|
-
Task.table_name = 'dbo.tasks'
|
49
|
-
assert Task.table_exists?, 'Tasks table name of dbo.tasks should return true for exists.'
|
50
|
-
ensure
|
51
|
-
Task.table_name = 'tasks'
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'for database version' do
|
56
|
-
|
57
|
-
setup do
|
58
|
-
@version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP
|
59
|
-
@supported_version = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS
|
60
|
-
@sqlserver_2000_string = "Microsoft SQL Server 2000 - 8.00.2039 (Intel X86)"
|
61
|
-
@sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)"
|
62
|
-
@sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)"
|
63
|
-
end
|
64
|
-
|
65
|
-
should 'return a string from #database_version that matches class regexp' do
|
66
|
-
assert_match @version_regexp, @connection.database_version
|
67
|
-
end
|
68
|
-
|
69
|
-
should 'return a 4 digit year fixnum for #database_year' do
|
70
|
-
assert_instance_of Fixnum, @connection.database_year
|
71
|
-
assert_contains @supported_version, @connection.database_year
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'for #unqualify_table_name and #unqualify_db_name' do
|
77
|
-
|
78
|
-
setup do
|
79
|
-
@expected_table_name = 'baz'
|
80
|
-
@expected_db_name = 'foo'
|
81
|
-
@first_second_table_names = ['[baz]','baz','[bar].[baz]','bar.baz']
|
82
|
-
@third_table_names = ['[foo].[bar].[baz]','foo.bar.baz']
|
83
|
-
@qualifed_table_names = @first_second_table_names + @third_table_names
|
84
|
-
end
|
85
|
-
|
86
|
-
should 'return clean table_name from #unqualify_table_name' do
|
87
|
-
@qualifed_table_names.each do |qtn|
|
88
|
-
assert_equal @expected_table_name,
|
89
|
-
@connection.send(:unqualify_table_name,qtn),
|
90
|
-
"This qualifed_table_name #{qtn} did not unqualify correctly."
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
should 'return nil from #unqualify_db_name when table_name is less than 2 qualified' do
|
95
|
-
@first_second_table_names.each do |qtn|
|
96
|
-
assert_equal nil, @connection.send(:unqualify_db_name,qtn),
|
97
|
-
"This qualifed_table_name #{qtn} did not return nil."
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
should 'return clean db_name from #unqualify_db_name when table is thrid level qualified' do
|
102
|
-
@third_table_names.each do |qtn|
|
103
|
-
assert_equal @expected_db_name,
|
104
|
-
@connection.send(:unqualify_db_name,qtn),
|
105
|
-
"This qualifed_table_name #{qtn} did not unqualify the db_name correctly."
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
should 'return true to #insert_sql? for inserts only' do
|
112
|
-
assert @connection.send(:insert_sql?,'INSERT...')
|
113
|
-
assert !@connection.send(:insert_sql?,'UPDATE...')
|
114
|
-
assert !@connection.send(:insert_sql?,'SELECT...')
|
115
|
-
end
|
116
|
-
|
117
|
-
context 'for #limited_update_conditions' do
|
118
|
-
|
119
|
-
should 'only match up to the first WHERE' do
|
120
|
-
where_sql = "TOP 1 WHERE ([posts].author_id = 1 and [posts].columnWHEREname = 2) ORDER BY posts.id"
|
121
|
-
assert_equal "WHERE bar IN (SELECT TOP 1 bar FROM foo WHERE ([posts].author_id = 1 and [posts].columnWHEREname = 2) ORDER BY posts.id)", @connection.limited_update_conditions(where_sql, 'foo', 'bar')
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
context 'for #sql_for_association_limiting?' do
|
127
|
-
|
128
|
-
should 'return false for simple selects with no GROUP BY and ORDER BY' do
|
129
|
-
assert !sql_for_association_limiting?("SELECT * FROM [posts]")
|
130
|
-
end
|
131
|
-
|
132
|
-
should 'return true to single SELECT, ideally a table/primarykey, that also has a GROUP BY and ORDER BY' do
|
133
|
-
assert sql_for_association_limiting?("SELECT [posts].id FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
|
134
|
-
end
|
135
|
-
|
136
|
-
should 'return false to single * wildcard SELECT that also has a GROUP BY and ORDER BY' do
|
137
|
-
assert !sql_for_association_limiting?("SELECT * FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
|
138
|
-
end
|
139
|
-
|
140
|
-
should 'return false to multiple columns in the select even when GROUP BY and ORDER BY are present' do
|
141
|
-
sql = "SELECT [accounts].credit_limit, firm_id FROM...GROUP BY firm_id ORDER BY firm_id"
|
142
|
-
assert !sql_for_association_limiting?(sql)
|
143
|
-
end
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
context 'for #get_table_name' do
|
148
|
-
|
149
|
-
should 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do
|
150
|
-
assert_equal '[funny_jokes]', @connection.send(:get_table_name,@basic_insert_sql)
|
151
|
-
assert_equal '[customers]', @connection.send(:get_table_name,@basic_update_sql)
|
152
|
-
assert_equal '[customers]', @connection.send(:get_table_name,@basic_select_sql)
|
153
|
-
end
|
154
|
-
|
155
|
-
end
|
156
|
-
|
157
|
-
context "for add_limit! within a scoped method call" do
|
158
|
-
setup do
|
159
|
-
@connection.stubs(:select_value).with(regexp_matches(/TotalRows/)).returns '100000000'
|
160
|
-
end
|
161
|
-
|
162
|
-
should 'not add any ordering if the scope doesn\'t have an order' do
|
163
|
-
assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1) AS tmp2', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10}, {})
|
164
|
-
end
|
165
|
-
|
166
|
-
should 'still add the default ordering if the scope doesn\'t have an order but the raw order option is there' do
|
167
|
-
assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [name] DESC) AS tmp2 ORDER BY [name]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10, :order => 'name'}, {})
|
168
|
-
end
|
169
|
-
|
170
|
-
should 'add scoped order options to the offset and limit sql' do
|
171
|
-
assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [id] DESC) AS tmp2 ORDER BY [id]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10}, {:order => 'id'})
|
172
|
-
end
|
173
|
-
|
174
|
-
should 'combine scoped order with raw order options in the offset and limit sql' do
|
175
|
-
assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [name] DESC, [id] DESC) AS tmp2 ORDER BY [name], [id]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10, :order => 'name'}, {:order => 'id'})
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
context 'dealing with various orders SQL snippets' do
|
180
|
-
|
181
|
-
setup do
|
182
|
-
@single_order = 'comments.id'
|
183
|
-
@single_order_with_desc = 'comments.id DESC'
|
184
|
-
@two_orders = 'comments.id, comments.post_id'
|
185
|
-
@two_orders_with_asc = 'comments.id, comments.post_id ASC'
|
186
|
-
@two_orders_with_desc_and_asc = 'comments.id DESC, comments.post_id ASC'
|
187
|
-
@two_duplicate_order_with_dif_dir = "id, id DESC"
|
188
|
-
end
|
189
|
-
|
190
|
-
should 'convert to an 2D array of column/direction arrays using #orders_and_dirs_set' do
|
191
|
-
assert_equal [['comments.id',nil]], orders_and_dirs_set('ORDER BY comments.id'), 'Needs to remove ORDER BY'
|
192
|
-
assert_equal [['comments.id',nil]], orders_and_dirs_set(@single_order)
|
193
|
-
assert_equal [['comments.id',nil],['comments.post_id',nil]], orders_and_dirs_set(@two_orders)
|
194
|
-
assert_equal [['comments.id',nil],['comments.post_id','ASC']], orders_and_dirs_set(@two_orders_with_asc)
|
195
|
-
assert_equal [['id',nil],['id','DESC']], orders_and_dirs_set(@two_duplicate_order_with_dif_dir)
|
196
|
-
end
|
197
|
-
|
198
|
-
should 'remove duplicate or maintain the same order by statements giving precedence to first using #add_order! method chain extension' do
|
199
|
-
assert_equal ' ORDER BY comments.id', add_order!(@single_order)
|
200
|
-
assert_equal ' ORDER BY comments.id DESC', add_order!(@single_order_with_desc)
|
201
|
-
assert_equal ' ORDER BY comments.id, comments.post_id', add_order!(@two_orders)
|
202
|
-
assert_equal ' ORDER BY comments.id DESC, comments.post_id ASC', add_order!(@two_orders_with_desc_and_asc)
|
203
|
-
assert_equal 'SELECT * FROM [developers] ORDER BY id', add_order!('id, developers.id DESC','SELECT * FROM [developers]')
|
204
|
-
assert_equal 'SELECT * FROM [developers] ORDER BY [developers].[id] DESC', add_order!('[developers].[id] DESC, id','SELECT * FROM [developers]')
|
205
|
-
end
|
206
|
-
|
207
|
-
should 'take all types of order options and convert them to MIN functions using #order_to_min_set' do
|
208
|
-
assert_equal 'MIN(comments.id)', order_to_min_set(@single_order)
|
209
|
-
assert_equal 'MIN(comments.id), MIN(comments.post_id)', order_to_min_set(@two_orders)
|
210
|
-
assert_equal 'MIN(comments.id) DESC', order_to_min_set(@single_order_with_desc)
|
211
|
-
assert_equal 'MIN(comments.id), MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_asc)
|
212
|
-
assert_equal 'MIN(comments.id) DESC, MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_desc_and_asc)
|
213
|
-
end
|
214
|
-
|
215
|
-
should 'leave order by alone when same column crosses two tables' do
|
216
|
-
assert_equal ' ORDER BY developers.name, projects.name', add_order!('developers.name, projects.name')
|
217
|
-
end
|
218
|
-
|
219
|
-
end
|
220
|
-
|
221
|
-
context 'with different language' do
|
222
|
-
|
223
|
-
teardown do
|
224
|
-
@connection.execute("SET LANGUAGE us_english") rescue nil
|
225
|
-
end
|
226
|
-
|
227
|
-
should_eventually 'do a date insertion when language is german' do
|
228
|
-
@connection.execute("SET LANGUAGE deutsch")
|
229
|
-
assert_nothing_raised do
|
230
|
-
Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
end
|
235
|
-
|
236
|
-
context 'testing #enable_default_unicode_types configuration' do
|
237
|
-
|
238
|
-
should 'use non-unicode types when set to false' do
|
239
|
-
with_enable_default_unicode_types(false) do
|
240
|
-
if sqlserver_2000?
|
241
|
-
assert_equal 'varchar', @connection.native_string_database_type
|
242
|
-
assert_equal 'text', @connection.native_text_database_type
|
243
|
-
elsif sqlserver_2005?
|
244
|
-
assert_equal 'varchar', @connection.native_string_database_type
|
245
|
-
assert_equal 'varchar(max)', @connection.native_text_database_type
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
should 'use unicode types when set to true' do
|
251
|
-
with_enable_default_unicode_types(true) do
|
252
|
-
if sqlserver_2000?
|
253
|
-
assert_equal 'nvarchar', @connection.native_string_database_type
|
254
|
-
assert_equal 'ntext', @connection.native_text_database_type
|
255
|
-
elsif sqlserver_2005?
|
256
|
-
assert_equal 'nvarchar', @connection.native_string_database_type
|
257
|
-
assert_equal 'nvarchar(max)', @connection.native_text_database_type
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
end
|
263
|
-
|
264
|
-
|
265
|
-
end
|
266
|
-
|
267
|
-
context 'For chronic data types' do
|
268
|
-
|
269
|
-
context 'with a usec' do
|
270
|
-
|
271
|
-
setup do
|
272
|
-
@time = Time.now
|
273
|
-
@db_datetime_003 = '2012-11-08 10:24:36.003'
|
274
|
-
@db_datetime_123 = '2012-11-08 10:24:36.123'
|
275
|
-
@all_datetimes = [@db_datetime_003, @db_datetime_123]
|
276
|
-
@all_datetimes.each do |datetime|
|
277
|
-
@connection.execute("INSERT INTO [sql_server_chronics] ([datetime]) VALUES('#{datetime}')")
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
teardown do
|
282
|
-
@all_datetimes.each do |datetime|
|
283
|
-
@connection.execute("DELETE FROM [sql_server_chronics] WHERE [datetime] = '#{datetime}'")
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
context 'finding existing DB objects' do
|
288
|
-
|
289
|
-
should 'find 003 millisecond in the DB with before and after casting' do
|
290
|
-
existing_003 = SqlServerChronic.find_by_datetime!(@db_datetime_003)
|
291
|
-
assert_equal @db_datetime_003, existing_003.datetime_before_type_cast if existing_003.datetime_before_type_cast.is_a?(String)
|
292
|
-
assert_equal 3000, existing_003.datetime.usec, 'A 003 millisecond in SQL Server is 3000 microseconds'
|
293
|
-
end
|
294
|
-
|
295
|
-
should 'find 123 millisecond in the DB with before and after casting' do
|
296
|
-
existing_123 = SqlServerChronic.find_by_datetime!(@db_datetime_123)
|
297
|
-
assert_equal @db_datetime_123, existing_123.datetime_before_type_cast if existing_123.datetime_before_type_cast.is_a?(String)
|
298
|
-
assert_equal 123000, existing_123.datetime.usec, 'A 123 millisecond in SQL Server is 123000 microseconds'
|
299
|
-
end
|
300
|
-
|
301
|
-
end
|
302
|
-
|
303
|
-
context 'saving new datetime objects' do
|
304
|
-
|
305
|
-
should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do
|
306
|
-
@time.stubs(:usec).returns(123456)
|
307
|
-
saved = SqlServerChronic.create!(:datetime => @time).reload
|
308
|
-
assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
|
309
|
-
assert_equal 123000, saved.datetime.usec
|
310
|
-
end
|
311
|
-
|
312
|
-
should 'truncate 3001 usec to just 003 in the DB cast back to 3000' do
|
313
|
-
@time.stubs(:usec).returns(3001)
|
314
|
-
saved = SqlServerChronic.create!(:datetime => @time).reload
|
315
|
-
assert_equal '003', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
|
316
|
-
assert_equal 3000, saved.datetime.usec
|
317
|
-
end
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
end
|
322
|
-
|
323
|
-
end
|
324
|
-
|
325
|
-
context 'For identity inserts' do
|
326
|
-
|
327
|
-
setup do
|
328
|
-
@identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')"
|
329
|
-
@identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')"
|
330
|
-
@identity_insert_sql_unordered = "INSERT INTO [funny_jokes] ([name],[id]) VALUES('Knock knock',420)"
|
331
|
-
end
|
332
|
-
|
333
|
-
should 'return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column' do
|
334
|
-
assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql)
|
335
|
-
assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted)
|
336
|
-
assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered)
|
337
|
-
end
|
338
|
-
|
339
|
-
should 'return false to #query_requires_identity_insert? for normal SQL' do
|
340
|
-
[@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql|
|
341
|
-
assert !@connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}"
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
should 'find identity column using #identity_column' do
|
346
|
-
joke_id_column = Joke.columns.detect { |c| c.name == 'id' }
|
347
|
-
assert_equal joke_id_column, @connection.send(:identity_column,Joke.table_name)
|
348
|
-
end
|
349
|
-
|
350
|
-
should 'return nil when calling #identity_column for a table_name with no identity' do
|
351
|
-
assert_nil @connection.send(:identity_column,Subscriber.table_name)
|
352
|
-
end
|
353
|
-
|
354
|
-
should 'return retreive auto incremented id column' do
|
355
|
-
joke = Joke.new
|
356
|
-
joke.id = 999
|
357
|
-
joke.save
|
358
|
-
assert_equal 999, joke.id
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
context 'For Quoting' do
|
363
|
-
|
364
|
-
should 'return 1 for #quoted_true' do
|
365
|
-
assert_equal '1', @connection.quoted_true
|
366
|
-
end
|
367
|
-
|
368
|
-
should 'return 0 for #quoted_false' do
|
369
|
-
assert_equal '0', @connection.quoted_false
|
370
|
-
end
|
371
|
-
|
372
|
-
should 'not escape backslash characters like abstract adapter' do
|
373
|
-
string_with_backslashs = "\\n"
|
374
|
-
assert_equal string_with_backslashs, @connection.quote_string(string_with_backslashs)
|
375
|
-
end
|
376
|
-
|
377
|
-
should 'quote column names with brackets' do
|
378
|
-
assert_equal '[foo]', @connection.quote_column_name(:foo)
|
379
|
-
assert_equal '[foo]', @connection.quote_column_name('foo')
|
380
|
-
assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
|
381
|
-
end
|
382
|
-
|
383
|
-
should 'not quote already quoted column names with brackets' do
|
384
|
-
assert_equal '[foo]', @connection.quote_column_name('[foo]')
|
385
|
-
assert_equal '[foo].[bar]', @connection.quote_column_name('[foo].[bar]')
|
386
|
-
end
|
387
|
-
|
388
|
-
should 'quote table names like columns' do
|
389
|
-
assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
|
390
|
-
assert_equal '[foo].[bar].[baz]', @connection.quote_column_name('foo.bar.baz')
|
391
|
-
end
|
392
|
-
|
393
|
-
end
|
394
|
-
|
395
|
-
context 'When disableing referential integrity' do
|
396
|
-
|
397
|
-
setup do
|
398
|
-
@parent = FkTestHasPk.create!
|
399
|
-
@member = FkTestHasFk.create!(:fk_id => @parent.id)
|
400
|
-
end
|
401
|
-
|
402
|
-
should 'NOT ALLOW by default the deletion of a referenced parent' do
|
403
|
-
FkTestHasPk.connection.disable_referential_integrity { }
|
404
|
-
assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
|
405
|
-
end
|
406
|
-
|
407
|
-
should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do
|
408
|
-
FkTestHasPk.connection.disable_referential_integrity { @parent.destroy }
|
409
|
-
end
|
410
|
-
|
411
|
-
should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do
|
412
|
-
assert_raise(ActiveRecord::StatementInvalid) do
|
413
|
-
FkTestHasPk.connection.disable_referential_integrity { }
|
414
|
-
@parent.destroy
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
end
|
419
|
-
|
420
|
-
context 'For DatabaseStatements' do
|
421
|
-
|
422
|
-
context "finding out what user_options are available" do
|
423
|
-
|
424
|
-
should "run the database consistency checker useroptions command" do
|
425
|
-
@connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns []
|
426
|
-
@connection.user_options
|
427
|
-
end
|
428
|
-
|
429
|
-
should "return a underscored key hash with indifferent access of the results" do
|
430
|
-
@connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns [['some', 'thing'], ['isolation level', 'read uncommitted']]
|
431
|
-
uo = @connection.user_options
|
432
|
-
assert_equal 2, uo.keys.size
|
433
|
-
assert_equal 'thing', uo['some']
|
434
|
-
assert_equal 'thing', uo[:some]
|
435
|
-
assert_equal 'read uncommitted', uo['isolation_level']
|
436
|
-
assert_equal 'read uncommitted', uo[:isolation_level]
|
437
|
-
end
|
438
|
-
|
439
|
-
end
|
440
|
-
|
441
|
-
context "altering isolation levels" do
|
442
|
-
|
443
|
-
should "barf if the requested isolation level is not valid" do
|
444
|
-
assert_raise(ArgumentError) do
|
445
|
-
@connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
|
-
context "with a valid isolation level" do
|
450
|
-
|
451
|
-
setup do
|
452
|
-
@t1 = tasks(:first_task)
|
453
|
-
@t2 = tasks(:another_task)
|
454
|
-
assert @t1, 'Tasks :first_task should be in AR fixtures'
|
455
|
-
assert @t2, 'Tasks :another_task should be in AR fixtures'
|
456
|
-
good_isolation_level = @connection.user_options[:isolation_level].blank? || @connection.user_options[:isolation_level] =~ /read committed/i
|
457
|
-
assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options[:isolation_level].inspect}"
|
458
|
-
end
|
459
|
-
|
460
|
-
should 'allow #run_with_isolation_level to not take a block to set it' do
|
461
|
-
begin
|
462
|
-
@connection.run_with_isolation_level 'READ UNCOMMITTED'
|
463
|
-
assert_match %r|read uncommitted|i, @connection.user_options[:isolation_level]
|
464
|
-
ensure
|
465
|
-
@connection.run_with_isolation_level 'READ COMMITTED'
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
should 'return block value using #run_with_isolation_level' do
|
470
|
-
assert_same_elements Task.find(:all), @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all) }
|
471
|
-
end
|
472
|
-
|
473
|
-
should 'pass a read uncommitted isolation level test' do
|
474
|
-
assert_nil @t2.starting, 'Fixture should have this empty.'
|
475
|
-
begin
|
476
|
-
Task.transaction do
|
477
|
-
@t2.starting = Time.now
|
478
|
-
@t2.save
|
479
|
-
@dirty_t2 = @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) }
|
480
|
-
raise ActiveRecord::ActiveRecordError
|
481
|
-
end
|
482
|
-
rescue
|
483
|
-
'Do Nothing'
|
484
|
-
end
|
485
|
-
assert @dirty_t2, 'Should have a Task record from within block above.'
|
486
|
-
assert @dirty_t2.starting, 'Should have a dirty date.'
|
487
|
-
assert_nil Task.find(@t2.id).starting, 'Should be nil again from botched transaction above.'
|
488
|
-
end
|
489
|
-
|
490
|
-
end
|
491
|
-
|
492
|
-
end
|
493
|
-
|
494
|
-
end
|
495
|
-
|
496
|
-
context 'For SchemaStatements' do
|
497
|
-
|
498
|
-
context 'returning from #type_to_sql' do
|
499
|
-
|
500
|
-
should 'create integers when no limit supplied' do
|
501
|
-
assert_equal 'integer', @connection.type_to_sql(:integer)
|
502
|
-
end
|
503
|
-
|
504
|
-
should 'create integers when limit is 4' do
|
505
|
-
assert_equal 'integer', @connection.type_to_sql(:integer, 4)
|
506
|
-
end
|
507
|
-
|
508
|
-
should 'create integers when limit is 3' do
|
509
|
-
assert_equal 'integer', @connection.type_to_sql(:integer, 3)
|
510
|
-
end
|
511
|
-
|
512
|
-
should 'create smallints when limit is less than 3' do
|
513
|
-
assert_equal 'smallint', @connection.type_to_sql(:integer, 2)
|
514
|
-
assert_equal 'smallint', @connection.type_to_sql(:integer, 1)
|
515
|
-
end
|
516
|
-
|
517
|
-
should 'create bigints when limit is greateer than 4' do
|
518
|
-
assert_equal 'bigint', @connection.type_to_sql(:integer, 5)
|
519
|
-
assert_equal 'bigint', @connection.type_to_sql(:integer, 6)
|
520
|
-
assert_equal 'bigint', @connection.type_to_sql(:integer, 7)
|
521
|
-
assert_equal 'bigint', @connection.type_to_sql(:integer, 8)
|
522
|
-
end
|
523
|
-
|
524
|
-
should 'create floats when no limit supplied' do
|
525
|
-
assert_equal 'float(8)', @connection.type_to_sql(:float)
|
526
|
-
end
|
527
|
-
|
528
|
-
should 'create floats when limit is supplied' do
|
529
|
-
assert_equal 'float(27)', @connection.type_to_sql(:float, 27)
|
530
|
-
end
|
531
|
-
|
532
|
-
end
|
533
|
-
|
534
|
-
end
|
535
|
-
|
536
|
-
context 'For indexes' do
|
537
|
-
|
538
|
-
setup do
|
539
|
-
@desc_index_name = 'idx_credit_limit_test_desc'
|
540
|
-
@connection.execute "CREATE INDEX #{@desc_index_name} ON accounts (credit_limit DESC)"
|
541
|
-
end
|
542
|
-
|
543
|
-
teardown do
|
544
|
-
@connection.execute "DROP INDEX accounts.#{@desc_index_name}"
|
545
|
-
end
|
546
|
-
|
547
|
-
should 'have indexes with descending order' do
|
548
|
-
assert @connection.indexes('accounts').detect { |i| i.name == @desc_index_name }
|
549
|
-
end
|
550
|
-
|
551
|
-
end
|
552
|
-
|
553
|
-
context 'For views' do
|
554
|
-
|
555
|
-
context 'using @connection.views' do
|
556
|
-
|
557
|
-
should 'return an array' do
|
558
|
-
assert_instance_of Array, @connection.views
|
559
|
-
end
|
560
|
-
|
561
|
-
should 'find CustomersView table name' do
|
562
|
-
assert_contains @connection.views, 'customers_view'
|
563
|
-
end
|
564
|
-
|
565
|
-
should 'not contain system views' do
|
566
|
-
systables = ['sysconstraints','syssegments']
|
567
|
-
systables.each do |systable|
|
568
|
-
assert !@connection.views.include?(systable), "This systable #{systable} should not be in the views array."
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
should 'allow the connection.view_information method to return meta data on the view' do
|
573
|
-
view_info = @connection.view_information('customers_view')
|
574
|
-
assert_equal('customers_view', view_info['TABLE_NAME'])
|
575
|
-
assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION'])
|
576
|
-
end
|
577
|
-
|
578
|
-
should 'allow the connection.view_table_name method to return true table_name for the view' do
|
579
|
-
assert_equal 'customers', @connection.view_table_name('customers_view')
|
580
|
-
assert_equal 'topics', @connection.view_table_name('topics'), 'No view here, the same table name should come back.'
|
581
|
-
end
|
582
|
-
|
583
|
-
end
|
584
|
-
|
585
|
-
context 'used by a class for table_name' do
|
586
|
-
|
587
|
-
context 'with same column names' do
|
588
|
-
|
589
|
-
should 'have matching column objects' do
|
590
|
-
columns = ['id','name','balance']
|
591
|
-
assert !CustomersView.columns.blank?
|
592
|
-
assert_equal columns.size, CustomersView.columns.size
|
593
|
-
columns.each do |colname|
|
594
|
-
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
|
595
|
-
CustomersView.columns_hash[colname],
|
596
|
-
"Column name #{colname.inspect} was not found in these columns #{CustomersView.columns.map(&:name).inspect}"
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
should 'find identity column' do
|
601
|
-
assert CustomersView.columns_hash['id'].primary
|
602
|
-
assert CustomersView.columns_hash['id'].is_identity?
|
603
|
-
end
|
604
|
-
|
605
|
-
should 'find default values' do
|
606
|
-
assert_equal 0, CustomersView.new.balance
|
607
|
-
end
|
608
|
-
|
609
|
-
should 'respond true to table_exists?' do
|
610
|
-
assert CustomersView.table_exists?
|
611
|
-
end
|
612
|
-
|
613
|
-
should 'have correct table name for all column objects' do
|
614
|
-
assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' },
|
615
|
-
CustomersView.columns.map(&:table_name).inspect
|
616
|
-
end
|
617
|
-
|
618
|
-
end
|
619
|
-
|
620
|
-
context 'with aliased column names' do
|
621
|
-
|
622
|
-
should 'have matching column objects' do
|
623
|
-
columns = ['id','pretend_null']
|
624
|
-
assert !StringDefaultsView.columns.blank?
|
625
|
-
assert_equal columns.size, StringDefaultsView.columns.size
|
626
|
-
columns.each do |colname|
|
627
|
-
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
|
628
|
-
StringDefaultsView.columns_hash[colname],
|
629
|
-
"Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}"
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
633
|
-
should 'find identity column' do
|
634
|
-
assert StringDefaultsView.columns_hash['id'].primary
|
635
|
-
assert StringDefaultsView.columns_hash['id'].is_identity?
|
636
|
-
end
|
637
|
-
|
638
|
-
should 'find default values' do
|
639
|
-
assert_equal 'null', StringDefaultsView.new.pretend_null,
|
640
|
-
StringDefaultsView.columns_hash['pretend_null'].inspect
|
641
|
-
end
|
642
|
-
|
643
|
-
should 'respond true to table_exists?' do
|
644
|
-
assert StringDefaultsView.table_exists?
|
645
|
-
end
|
646
|
-
|
647
|
-
should 'have correct table name for all column objects' do
|
648
|
-
assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' },
|
649
|
-
StringDefaultsView.columns.map(&:table_name).inspect
|
650
|
-
end
|
651
|
-
|
652
|
-
end
|
653
|
-
|
654
|
-
end
|
655
|
-
|
656
|
-
context 'doing identity inserts' do
|
657
|
-
|
658
|
-
setup do
|
659
|
-
@view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)"
|
660
|
-
end
|
661
|
-
|
662
|
-
should 'respond true/tablename to #query_requires_identity_insert?' do
|
663
|
-
assert_equal '[customers_view]', @connection.send(:query_requires_identity_insert?,@view_insert_sql)
|
664
|
-
end
|
665
|
-
|
666
|
-
should 'be able to do an identity insert' do
|
667
|
-
assert_nothing_raised { @connection.execute(@view_insert_sql) }
|
668
|
-
assert CustomersView.find(420)
|
669
|
-
end
|
670
|
-
|
671
|
-
end
|
672
|
-
|
673
|
-
context 'that have more than 4000 chars for their defintion' do
|
674
|
-
|
675
|
-
should 'cope with null returned for the defintion' do
|
676
|
-
assert_nothing_raised() { StringDefaultsBigView.columns }
|
677
|
-
end
|
678
|
-
|
679
|
-
should 'using alternate view defintion still be able to find real default' do
|
680
|
-
assert_equal 'null', StringDefaultsBigView.new.pretend_null,
|
681
|
-
StringDefaultsBigView.columns_hash['pretend_null'].inspect
|
682
|
-
end
|
683
|
-
|
684
|
-
end
|
685
|
-
|
686
|
-
end
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
private
|
691
|
-
|
692
|
-
def sql_for_association_limiting?(sql)
|
693
|
-
@connection.send :sql_for_association_limiting?, sql
|
694
|
-
end
|
695
|
-
|
696
|
-
def orders_and_dirs_set(order)
|
697
|
-
@connection.send :orders_and_dirs_set, order
|
698
|
-
end
|
699
|
-
|
700
|
-
def add_order!(order,sql='')
|
701
|
-
ActiveRecord::Base.send :add_order!, sql, order, nil
|
702
|
-
sql
|
703
|
-
end
|
704
|
-
|
705
|
-
def add_limit!(sql, options, scope = :auto)
|
706
|
-
ActiveRecord::Base.send :add_limit!, sql, options, scope
|
707
|
-
sql
|
708
|
-
end
|
709
|
-
|
710
|
-
def order_to_min_set(order)
|
711
|
-
@connection.send :order_to_min_set, order
|
712
|
-
end
|
713
|
-
|
714
|
-
def with_enable_default_unicode_types(setting)
|
715
|
-
old_setting = ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types
|
716
|
-
old_text = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type
|
717
|
-
old_string = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type
|
718
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = setting
|
719
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = nil
|
720
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = nil
|
721
|
-
yield
|
722
|
-
ensure
|
723
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = old_setting
|
724
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = old_text
|
725
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = old_string
|
726
|
-
end
|
727
|
-
|
728
|
-
end
|
729
|
-
|
730
|
-
|
731
|
-
class AdapterTest < ActiveRecord::TestCase
|
732
|
-
|
733
|
-
COERCED_TESTS = [
|
734
|
-
:test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas,
|
735
|
-
:test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
|
736
|
-
]
|
737
|
-
|
738
|
-
include SqlserverCoercedTest
|
739
|
-
|
740
|
-
def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
|
741
|
-
sql_inject = "1 select * from schema"
|
742
|
-
connection = ActiveRecord::Base.connection
|
743
|
-
assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
|
744
|
-
assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
|
745
|
-
end
|
746
|
-
|
747
|
-
def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
|
748
|
-
sql_inject = "1, 7 procedure help()"
|
749
|
-
connection = ActiveRecord::Base.connection
|
750
|
-
assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
|
751
|
-
assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7) }
|
752
|
-
assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
|
753
|
-
end
|
754
|
-
|
755
|
-
end
|