activerecord-sqlserver-adapter 2.3.24 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,148 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/reply'
|
3
|
-
|
4
|
-
class ConnectionTestSqlserver < ActiveRecord::TestCase
|
5
|
-
|
6
|
-
self.use_transactional_fixtures = false
|
7
|
-
|
8
|
-
fixtures :topics, :accounts
|
9
|
-
|
10
|
-
def setup
|
11
|
-
@connection = ActiveRecord::Base.connection
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
should 'affect rows' do
|
16
|
-
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
|
17
|
-
updated = Topic.update(topic_data.keys, topic_data.values)
|
18
|
-
assert_equal 2, updated.size
|
19
|
-
assert_equal "1 updated", Topic.find(1).content
|
20
|
-
assert_equal "2 updated", Topic.find(2).content
|
21
|
-
assert_equal 2, Topic.delete([1, 2])
|
22
|
-
end
|
23
|
-
|
24
|
-
should 'allow usage of :database connection option to remove setting from dsn' do
|
25
|
-
assert_equal 'activerecord_unittest', @connection.current_database
|
26
|
-
begin
|
27
|
-
@connection.use_database('activerecord_unittest2')
|
28
|
-
assert_equal 'activerecord_unittest2', @connection.current_database
|
29
|
-
ensure
|
30
|
-
@connection.use_database
|
31
|
-
assert_equal 'activerecord_unittest', @connection.current_database, 'Would default back to connection options'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'ODBC connection management' do
|
36
|
-
|
37
|
-
should 'return finished ODBC statement handle from #execute without block' do
|
38
|
-
assert_all_odbc_statements_used_are_closed do
|
39
|
-
@connection.execute('SELECT * FROM [topics]')
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
should 'finish ODBC statement handle from #execute with block' do
|
44
|
-
assert_all_odbc_statements_used_are_closed do
|
45
|
-
@connection.execute('SELECT * FROM [topics]') { }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
should 'finish connection from #raw_select' do
|
50
|
-
assert_all_odbc_statements_used_are_closed do
|
51
|
-
@connection.send(:raw_select,'SELECT * FROM [topics]')
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
should 'execute without block closes statement' do
|
56
|
-
assert_all_odbc_statements_used_are_closed do
|
57
|
-
@connection.execute("SELECT 1")
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
should 'execute with block closes statement' do
|
62
|
-
assert_all_odbc_statements_used_are_closed do
|
63
|
-
@connection.execute("SELECT 1") do |sth|
|
64
|
-
assert !sth.finished?, "Statement should still be alive within block"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
should 'insert with identity closes statement' do
|
70
|
-
assert_all_odbc_statements_used_are_closed do
|
71
|
-
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
should 'insert without identity closes statement' do
|
76
|
-
assert_all_odbc_statements_used_are_closed do
|
77
|
-
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
should 'active closes statement' do
|
82
|
-
assert_all_odbc_statements_used_are_closed do
|
83
|
-
@connection.active?
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
end if connection_mode_odbc?
|
88
|
-
|
89
|
-
|
90
|
-
context 'Connection management' do
|
91
|
-
|
92
|
-
setup do
|
93
|
-
assert @connection.active?
|
94
|
-
end
|
95
|
-
|
96
|
-
should 'be able to disconnect and reconnect at will' do
|
97
|
-
@connection.disconnect!
|
98
|
-
assert !@connection.active?
|
99
|
-
@connection.reconnect!
|
100
|
-
assert @connection.active?
|
101
|
-
end
|
102
|
-
|
103
|
-
should 'auto reconnect when setting is on' do
|
104
|
-
with_auto_connect(true) do
|
105
|
-
@connection.disconnect!
|
106
|
-
assert_nothing_raised() { Topic.count }
|
107
|
-
assert @connection.active?
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
should 'not auto reconnect when setting is off' do
|
112
|
-
with_auto_connect(false) do
|
113
|
-
@connection.disconnect!
|
114
|
-
assert_raise(ActiveRecord::StatementInvalid) { Topic.count }
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def assert_all_odbc_statements_used_are_closed(&block)
|
125
|
-
odbc = @connection.raw_connection.class.parent
|
126
|
-
existing_handles = []
|
127
|
-
ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h }
|
128
|
-
existing_handle_ids = existing_handles.map(&:object_id)
|
129
|
-
assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed"
|
130
|
-
GC.disable
|
131
|
-
yield
|
132
|
-
used_handles = []
|
133
|
-
ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) }
|
134
|
-
assert used_handles.size > 0, "No statements were used within given block"
|
135
|
-
assert used_handles.all?(&:finished?), "Statement should have been closed within given block"
|
136
|
-
ensure
|
137
|
-
GC.enable
|
138
|
-
end
|
139
|
-
|
140
|
-
def with_auto_connect(boolean)
|
141
|
-
existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect
|
142
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean
|
143
|
-
yield
|
144
|
-
ensure
|
145
|
-
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/post'
|
3
|
-
require 'models/author'
|
4
|
-
require 'models/comment'
|
5
|
-
|
6
|
-
class EagerAssociationTestSqlserver < ActiveRecord::TestCase
|
7
|
-
end
|
8
|
-
|
9
|
-
class EagerAssociationTest < ActiveRecord::TestCase
|
10
|
-
|
11
|
-
COERCED_TESTS = [
|
12
|
-
:test_count_with_include,
|
13
|
-
:test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions,
|
14
|
-
:test_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
|
15
|
-
]
|
16
|
-
|
17
|
-
include SqlserverCoercedTest
|
18
|
-
|
19
|
-
fixtures :authors, :posts, :comments
|
20
|
-
|
21
|
-
def test_coerced_test_count_with_include
|
22
|
-
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_coerced_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
|
26
|
-
assert_queries(2) do
|
27
|
-
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
|
28
|
-
:conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ])
|
29
|
-
assert_equal 0, posts.size
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_coerced_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
|
34
|
-
assert_queries(2) do
|
35
|
-
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
|
36
|
-
:conditions => { 'authors.name' => 'David', 'comments.body' => 'go crazy' })
|
37
|
-
assert_equal 0, posts.size
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
|
3
|
-
class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
@klass = ActiveRecord::Base
|
7
|
-
end
|
8
|
-
|
9
|
-
should 'execute a simple procedure' do
|
10
|
-
tables = @klass.execute_procedure :sp_tables
|
11
|
-
assert_instance_of Array, tables
|
12
|
-
assert tables.first.respond_to?(:keys)
|
13
|
-
end
|
14
|
-
|
15
|
-
should 'take parameter arguments' do
|
16
|
-
tables = @klass.execute_procedure :sp_tables, 'sql_server_chronics'
|
17
|
-
table_info = tables.first
|
18
|
-
assert_equal 1, tables.size
|
19
|
-
assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}"
|
20
|
-
assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}"
|
21
|
-
end
|
22
|
-
|
23
|
-
should 'allow multiple result sets to be returned' do
|
24
|
-
results1, results2 = @klass.execute_procedure('sp_helpconstraint','accounts')
|
25
|
-
assert_instance_of Array, results1
|
26
|
-
assert results1.first.respond_to?(:keys)
|
27
|
-
assert results1.first['Object Name']
|
28
|
-
assert_instance_of Array, results2
|
29
|
-
assert results2.first.respond_to?(:keys)
|
30
|
-
assert results2.first['constraint_name']
|
31
|
-
assert results2.first['constraint_type']
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/company'
|
3
|
-
|
4
|
-
class InheritanceTestSqlserver < ActiveRecord::TestCase
|
5
|
-
end
|
6
|
-
|
7
|
-
class InheritanceTest < ActiveRecord::TestCase
|
8
|
-
|
9
|
-
COERCED_TESTS = [
|
10
|
-
:test_eager_load_belongs_to_primary_key_quoting,
|
11
|
-
:test_a_bad_type_column
|
12
|
-
]
|
13
|
-
|
14
|
-
include SqlserverCoercedTest
|
15
|
-
|
16
|
-
def test_coerced_test_eager_load_belongs_to_primary_key_quoting
|
17
|
-
assert_sql(/\(\[companies\].\[id\] = 1\)/) do
|
18
|
-
Account.find(1, :include => :firm)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_coerced_test_a_bad_type_column
|
23
|
-
Company.connection.insert "INSERT INTO [companies] ([id], #{QUOTED_TYPE}, [name]) VALUES(100, 'bad_class!', 'Not happening')"
|
24
|
-
assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/developer'
|
3
|
-
|
4
|
-
class MethodScopingTestSqlServer < ActiveRecord::TestCase
|
5
|
-
end
|
6
|
-
|
7
|
-
class NestedScopingTest < ActiveRecord::TestCase
|
8
|
-
|
9
|
-
COERCED_TESTS = [:test_merged_scoped_find]
|
10
|
-
|
11
|
-
include SqlserverCoercedTest
|
12
|
-
|
13
|
-
fixtures :developers
|
14
|
-
|
15
|
-
def test_coerced_test_merged_scoped_find
|
16
|
-
poor_jamis = developers(:poor_jamis)
|
17
|
-
Developer.with_scope(:find => { :conditions => "salary < 100000" }) do
|
18
|
-
Developer.with_scope(:find => { :offset => 1, :order => 'id asc' }) do
|
19
|
-
assert_sql /ORDER BY id ASC/ do
|
20
|
-
assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
|
@@ -1,108 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/person'
|
3
|
-
|
4
|
-
class MigrationTestSqlserver < ActiveRecord::TestCase
|
5
|
-
|
6
|
-
def setup
|
7
|
-
@connection = ActiveRecord::Base.connection
|
8
|
-
end
|
9
|
-
|
10
|
-
context 'For transactions' do
|
11
|
-
|
12
|
-
setup do
|
13
|
-
@trans_test_table1 = 'sqlserver_trans_table1'
|
14
|
-
@trans_test_table2 = 'sqlserver_trans_table2'
|
15
|
-
@trans_tables = [@trans_test_table1,@trans_test_table2]
|
16
|
-
end
|
17
|
-
|
18
|
-
teardown do
|
19
|
-
@trans_tables.each do |table_name|
|
20
|
-
ActiveRecord::Migration.drop_table(table_name) if @connection.tables.include?(table_name)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
should 'not create a tables if error in migrations' do
|
25
|
-
begin
|
26
|
-
ActiveRecord::Migrator.up(SQLSERVER_MIGRATIONS_ROOT+'/transaction_table')
|
27
|
-
rescue Exception => e
|
28
|
-
assert_match %r|this and all later migrations canceled|, e.message
|
29
|
-
end
|
30
|
-
assert_does_not_contain @trans_test_table1, @connection.tables
|
31
|
-
assert_does_not_contain @trans_test_table2, @connection.tables
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
context 'For changing column' do
|
37
|
-
|
38
|
-
should 'not raise exception when column contains default constraint' do
|
39
|
-
lock_version_column = Person.columns_hash['lock_version']
|
40
|
-
assert_equal :integer, lock_version_column.type
|
41
|
-
assert lock_version_column.default.present?
|
42
|
-
assert_nothing_raised { @connection.change_column 'people', 'lock_version', :string }
|
43
|
-
Person.reset_column_information
|
44
|
-
lock_version_column = Person.columns_hash['lock_version']
|
45
|
-
assert_equal :string, lock_version_column.type
|
46
|
-
assert lock_version_column.default.nil?
|
47
|
-
end
|
48
|
-
|
49
|
-
should 'not drop the default contraint if just renaming' do
|
50
|
-
find_default = lambda do
|
51
|
-
@connection.select_all("EXEC sp_helpconstraint 'defaults','nomsg'").select do |row|
|
52
|
-
row['constraint_type'] == "DEFAULT on column decimal_number"
|
53
|
-
end.last
|
54
|
-
end
|
55
|
-
default_before = find_default.call
|
56
|
-
@connection.change_column :defaults, :decimal_number, :decimal, :precision => 4
|
57
|
-
default_after = find_default.call
|
58
|
-
assert default_after
|
59
|
-
assert_equal default_before['constraint_keys'], default_after['constraint_keys']
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
class MigrationTest < ActiveRecord::TestCase
|
68
|
-
|
69
|
-
COERCED_TESTS = [:test_add_column_not_null_without_default]
|
70
|
-
|
71
|
-
include SqlserverCoercedTest
|
72
|
-
|
73
|
-
def test_coerced_test_add_column_not_null_without_default
|
74
|
-
Person.connection.create_table :testings do |t|
|
75
|
-
t.column :foo, :string
|
76
|
-
t.column :bar, :string, :null => false
|
77
|
-
end
|
78
|
-
assert_raises(ActiveRecord::StatementInvalid) do
|
79
|
-
Person.connection.execute "INSERT INTO [testings] ([foo], [bar]) VALUES ('hello', NULL)"
|
80
|
-
end
|
81
|
-
ensure
|
82
|
-
Person.connection.drop_table :testings rescue nil
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
|
-
class ChangeTableMigrationsTest < ActiveRecord::TestCase
|
89
|
-
|
90
|
-
COERCED_TESTS = [:test_string_creates_string_column]
|
91
|
-
|
92
|
-
include SqlserverCoercedTest
|
93
|
-
|
94
|
-
def test_coerced_string_creates_string_column
|
95
|
-
with_change_table do |t|
|
96
|
-
@connection.expects(:add_column).with(:delete_me, :foo, coerced_string_column, {})
|
97
|
-
@connection.expects(:add_column).with(:delete_me, :bar, coerced_string_column, {})
|
98
|
-
t.string :foo, :bar
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def coerced_string_column
|
103
|
-
"#{Person.connection.native_string_database_type}(255)"
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
|
3
|
-
class NamedScopeTestSqlserver < ActiveRecord::TestCase
|
4
|
-
end
|
5
|
-
|
6
|
-
class NamedScopeTest < ActiveRecord::TestCase
|
7
|
-
|
8
|
-
COERCED_TESTS = [:test_named_scopes_honor_current_scopes_from_when_defined]
|
9
|
-
|
10
|
-
include SqlserverCoercedTest
|
11
|
-
|
12
|
-
# See: http://github.com/rails/rails/commit/0dd2f96f5c90f8abacb0fe0757ef7e5db4a4d501#comment_37025
|
13
|
-
# The orig test is a little brittle and fails on other adapters that do not explicitly fall back to a secondary
|
14
|
-
# sort of id ASC. Since there are duplicate records with comments_count equal to one another. I have found that
|
15
|
-
# named_scope :ranked_by_comments, :order => "comments_count DESC, id ASC" fixes the ambiguity.
|
16
|
-
def test_coerced_test_named_scopes_honor_current_scopes_from_when_defined
|
17
|
-
assert true
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
end
|
@@ -1,108 +0,0 @@
|
|
1
|
-
require 'cases/sqlserver_helper'
|
2
|
-
require 'models/book'
|
3
|
-
|
4
|
-
class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
|
5
|
-
|
6
|
-
class Account < ActiveRecord::Base; end
|
7
|
-
|
8
|
-
def setup
|
9
|
-
@connection = ActiveRecord::Base.connection
|
10
|
-
end
|
11
|
-
|
12
|
-
context 'When selecting with limit' do
|
13
|
-
|
14
|
-
setup do
|
15
|
-
@select_sql = 'SELECT * FROM schema'
|
16
|
-
end
|
17
|
-
|
18
|
-
should 'alter SQL to limit number of records returned' do
|
19
|
-
options = { :limit => 10 }
|
20
|
-
assert_equal('SELECT TOP 10 * FROM schema', @connection.add_limit_offset!(@select_sql, options))
|
21
|
-
end
|
22
|
-
|
23
|
-
should 'only allow integers for limit' do
|
24
|
-
options = { :limit => 'ten' }
|
25
|
-
assert_raise(ArgumentError) {@connection.add_limit_offset!(@select_sql, options) }
|
26
|
-
end
|
27
|
-
|
28
|
-
should 'convert strings which look like integers to integers' do
|
29
|
-
options = { :limit => '42' }
|
30
|
-
assert_nothing_raised(ArgumentError) {@connection.add_limit_offset!(@select_sql, options)}
|
31
|
-
end
|
32
|
-
|
33
|
-
should 'not allow sql injection via limit' do
|
34
|
-
options = { :limit => '1 * FROM schema; DELETE * FROM table; SELECT TOP 10 *'}
|
35
|
-
assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'When selecting with limit and offset' do
|
41
|
-
|
42
|
-
setup do
|
43
|
-
@select_sql = 'SELECT * FROM books'
|
44
|
-
@subquery_select_sql = 'SELECT *, (SELECT TOP 1 id FROM books) AS book_id FROM books'
|
45
|
-
@books = (1..10).map {|i| Book.create!}
|
46
|
-
end
|
47
|
-
|
48
|
-
teardown do
|
49
|
-
@books.each {|b| b.destroy}
|
50
|
-
end
|
51
|
-
|
52
|
-
should 'have limit if offset is passed' do
|
53
|
-
options = { :offset => 1 }
|
54
|
-
assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
|
55
|
-
end
|
56
|
-
|
57
|
-
should 'only allow integers for offset' do
|
58
|
-
options = { :limit => 10, :offset => 'five' }
|
59
|
-
assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options)}
|
60
|
-
end
|
61
|
-
|
62
|
-
should 'convert strings which look like integers to integers' do
|
63
|
-
options = { :limit => 10, :offset => '5' }
|
64
|
-
assert_nothing_raised(ArgumentError) {@connection.add_limit_offset!(@select_sql, options)}
|
65
|
-
end
|
66
|
-
|
67
|
-
should 'alter SQL to limit number of records returned offset by specified amount' do
|
68
|
-
options = { :limit => 3, :offset => 5 }
|
69
|
-
expected_sql = "SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books) AS tmp1) AS tmp2"
|
70
|
-
assert_equal(expected_sql, @connection.add_limit_offset!(@select_sql, options))
|
71
|
-
end
|
72
|
-
|
73
|
-
should 'add locks to deepest sub select in limit offset sql that has a limited tally' do
|
74
|
-
options = { :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' }
|
75
|
-
expected_sql = "SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books WITH (NOLOCK)) AS tmp1) AS tmp2"
|
76
|
-
@connection.add_limit_offset! @select_sql, options
|
77
|
-
assert_equal expected_sql, @connection.add_lock!(@select_sql,options)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Not really sure what an offset sql injection might look like
|
81
|
-
should 'not allow sql injection via offset' do
|
82
|
-
options = { :limit => 10, :offset => '1 * FROM schema; DELETE * FROM table; SELECT TOP 10 *'}
|
83
|
-
assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
|
84
|
-
end
|
85
|
-
|
86
|
-
should 'not create invalid SQL with subquery SELECTs with TOP' do
|
87
|
-
options = { :limit => 5, :offset => 1 }
|
88
|
-
expected_sql = "SELECT * FROM (SELECT TOP 5 * FROM (SELECT TOP 6 *, (SELECT TOP 1 id FROM books) AS book_id FROM books) AS tmp1) AS tmp2"
|
89
|
-
assert_equal expected_sql, @connection.add_limit_offset!(@subquery_select_sql,options)
|
90
|
-
end
|
91
|
-
|
92
|
-
should 'add lock hints to tally sql if :lock option is present' do
|
93
|
-
assert_sql %r|SELECT TOP 1000000000 \* FROM \[people\] WITH \(NOLOCK\)| do
|
94
|
-
Person.all :limit => 5, :offset => 1, :lock => 'WITH (NOLOCK)'
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
should 'not add lock hints to tally sql if there is no :lock option' do
|
99
|
-
assert_sql %r|\(SELECT TOP 1000000000 \* FROM \[people\] \)| do
|
100
|
-
Person.all :limit => 5, :offset => 1
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|