rails-sqlserver-2000-2005-adapter 2.2.2 → 2.2.3
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 +11 -1
- data/README.rdoc +1 -1
- data/RUNNING_UNIT_TESTS +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +24 -11
- data/test/cases/adapter_test_sqlserver.rb +68 -25
- data/test/cases/offset_and_limit_test_sqlserver.rb +7 -0
- data/test/schema/sqlserver_specific_schema.rb +1 -1
- metadata +1 -1
data/CHANGELOG
CHANGED
@@ -4,9 +4,19 @@ MASTER
|
|
4
4
|
*
|
5
5
|
|
6
6
|
|
7
|
+
* 2.2.3 (December 5th, 2008)
|
8
|
+
|
9
|
+
* Changing back to using real table name in column_definitions. Makes sure views get back only the columns
|
10
|
+
that are defined for them with correct names, etc. Now supporting views by looking for NULL default and
|
11
|
+
then if table name is a view, perform a targeted with sub select to the real table name and column name
|
12
|
+
to find true default. [Ken Collins]
|
13
|
+
|
14
|
+
* Ensure that add_limit_offset! does not alter sub queries. [Erik Bryn]
|
15
|
+
|
16
|
+
|
7
17
|
2.2.2 (December 2nd, 2008)
|
8
18
|
|
9
|
-
* Add support for view defaults by making column_definitions use real table name for schema info.
|
19
|
+
* Add support for view defaults by making column_definitions use real table name for schema info. [Ken Collins]
|
10
20
|
|
11
21
|
* Include version in connection method and inspection. [Ken Collins]
|
12
22
|
|
data/README.rdoc
CHANGED
@@ -13,7 +13,7 @@ The SQL Server adapter for rails is back for ActiveRecord 2.2 and up! We are cur
|
|
13
13
|
* Pessimistic locking suppot. See the #add_lock! method for details.
|
14
14
|
* Enabled #case_sensitive_equality_operator used by unique validations.
|
15
15
|
* Unicode character support for nchar, nvarchar and ntext data types.
|
16
|
-
*
|
16
|
+
* View support for table names, identity inserts, and column defaults.
|
17
17
|
|
18
18
|
==== Date/Time Data Type Hinting
|
19
19
|
|
data/RUNNING_UNIT_TESTS
CHANGED
@@ -11,7 +11,7 @@ test/connections/<your database>/connection.rb.
|
|
11
11
|
The following gems need to be installed. Make sure you have gems.github.com as a
|
12
12
|
source. http://github.com/blog/97-github-loves-rubygems-1-2
|
13
13
|
|
14
|
-
* gem install
|
14
|
+
* gem install thoughtbot-shoulda -s http://gems.github.com
|
15
15
|
* gem install mocha
|
16
16
|
|
17
17
|
The tests of this adapter depend on the existence of rails checkout. All the tests
|
@@ -150,7 +150,7 @@ module ActiveRecord
|
|
150
150
|
class SQLServerAdapter < AbstractAdapter
|
151
151
|
|
152
152
|
ADAPTER_NAME = 'SQLServer'.freeze
|
153
|
-
VERSION = '2.2.
|
153
|
+
VERSION = '2.2.3'.freeze
|
154
154
|
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
|
155
155
|
SUPPORTED_VERSIONS = [2000,2005].freeze
|
156
156
|
LIMITABLE_TYPES = ['string','integer','float','char','nchar','varchar','nvarchar'].freeze
|
@@ -353,7 +353,7 @@ module ActiveRecord
|
|
353
353
|
end
|
354
354
|
# The business of adding limit/offset
|
355
355
|
if options[:limit] and options[:offset]
|
356
|
-
total_rows = select_value("SELECT count(*) as TotalRows from (#{sql.
|
356
|
+
total_rows = select_value("SELECT count(*) as TotalRows from (#{sql.sub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally").to_i
|
357
357
|
if (options[:limit] + options[:offset]) >= total_rows
|
358
358
|
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
|
359
359
|
end
|
@@ -457,6 +457,7 @@ module ActiveRecord
|
|
457
457
|
end
|
458
458
|
|
459
459
|
def view_information(table_name)
|
460
|
+
table_name = unqualify_table_name(table_name)
|
460
461
|
select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'"
|
461
462
|
end
|
462
463
|
|
@@ -827,6 +828,12 @@ module ActiveRecord
|
|
827
828
|
end
|
828
829
|
end
|
829
830
|
|
831
|
+
def views_real_column_name(table_name,column_name)
|
832
|
+
view_definition = view_information(table_name)['VIEW_DEFINITION']
|
833
|
+
match_data = view_definition.match(/([\w-]*)\s+as\s+pretend_null/im)
|
834
|
+
match_data ? match_data[1] : column_name
|
835
|
+
end
|
836
|
+
|
830
837
|
def order_to_min_set(order)
|
831
838
|
orders_dirs = orders_and_dirs_set(order)
|
832
839
|
orders_dirs.map do |o,d|
|
@@ -854,17 +861,13 @@ module ActiveRecord
|
|
854
861
|
|
855
862
|
def column_definitions(table_name)
|
856
863
|
db_name = unqualify_db_name(table_name)
|
857
|
-
|
858
|
-
passed_table_name = unqualify_table_name(table_name)
|
864
|
+
table_name = unqualify_table_name(table_name)
|
859
865
|
sql = %{
|
860
866
|
SELECT
|
861
867
|
columns.TABLE_NAME as table_name,
|
862
868
|
columns.COLUMN_NAME as name,
|
863
869
|
columns.DATA_TYPE as type,
|
864
|
-
|
865
|
-
WHEN columns.COLUMN_DEFAULT = '(null)' OR columns.COLUMN_DEFAULT = '(NULL)' THEN NULL
|
866
|
-
ELSE columns.COLUMN_DEFAULT
|
867
|
-
END as default_value,
|
870
|
+
columns.COLUMN_DEFAULT as default_value,
|
868
871
|
columns.NUMERIC_SCALE as numeric_scale,
|
869
872
|
columns.NUMERIC_PRECISION as numeric_precision,
|
870
873
|
CASE
|
@@ -880,13 +883,12 @@ module ActiveRecord
|
|
880
883
|
ELSE 1
|
881
884
|
END as is_identity
|
882
885
|
FROM #{db_name}INFORMATION_SCHEMA.COLUMNS columns
|
883
|
-
WHERE columns.TABLE_NAME = '#{
|
886
|
+
WHERE columns.TABLE_NAME = '#{table_name}'
|
884
887
|
ORDER BY columns.ordinal_position
|
885
888
|
}.gsub(/[ \t\r\n]+/,' ')
|
886
889
|
results = without_type_conversion { select(sql,nil,true) }
|
887
890
|
results.collect do |ci|
|
888
891
|
ci.symbolize_keys!
|
889
|
-
ci[:table_name] = passed_table_name
|
890
892
|
ci[:type] = case ci[:type]
|
891
893
|
when /^bit|image|text|ntext|datetime$/
|
892
894
|
ci[:type]
|
@@ -897,7 +899,18 @@ module ActiveRecord
|
|
897
899
|
else
|
898
900
|
ci[:type]
|
899
901
|
end
|
900
|
-
ci[:default_value]
|
902
|
+
if ci[:default_value].nil? && views.include?(table_name)
|
903
|
+
real_table_name = table_name_or_views_table_name(table_name)
|
904
|
+
real_column_name = views_real_column_name(table_name,ci[:name])
|
905
|
+
col_default_sql = "SELECT c.COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
|
906
|
+
ci[:default_value] = without_type_conversion { select_value(col_default_sql) }
|
907
|
+
end
|
908
|
+
ci[:default_value] = case ci[:default_value]
|
909
|
+
when nil, '(null)', '(NULL)'
|
910
|
+
nil
|
911
|
+
else
|
912
|
+
ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/)[1]
|
913
|
+
end
|
901
914
|
ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
|
902
915
|
ci
|
903
916
|
end
|
@@ -284,13 +284,12 @@ class AdapterTestSqlserver < ActiveRecord::TestCase
|
|
284
284
|
end
|
285
285
|
|
286
286
|
should 'NOT ALLOW by default the deletion of a referenced parent' do
|
287
|
+
FkTestHasPk.connection.disable_referential_integrity { }
|
287
288
|
assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
|
288
289
|
end
|
289
290
|
|
290
291
|
should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do
|
291
|
-
|
292
|
-
FkTestHasPk.connection.disable_referential_integrity { @parent.destroy }
|
293
|
-
end
|
292
|
+
FkTestHasPk.connection.disable_referential_integrity { @parent.destroy }
|
294
293
|
end
|
295
294
|
|
296
295
|
should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do
|
@@ -389,31 +388,75 @@ class AdapterTestSqlserver < ActiveRecord::TestCase
|
|
389
388
|
|
390
389
|
context 'used by a class for table_name' do
|
391
390
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
391
|
+
context 'with same column names' do
|
392
|
+
|
393
|
+
should 'have matching column objects' do
|
394
|
+
columns = ['id','name','balance']
|
395
|
+
assert !CustomersView.columns.blank?
|
396
|
+
assert_equal columns.size, CustomersView.columns.size
|
397
|
+
columns.each do |colname|
|
398
|
+
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
|
399
|
+
CustomersView.columns_hash[colname],
|
400
|
+
"Column name #{colname.inspect} was not found in these columns #{CustomersView.columns.map(&:name).inspect}"
|
401
|
+
end
|
397
402
|
end
|
403
|
+
|
404
|
+
should 'find identity column' do
|
405
|
+
assert CustomersView.columns_hash['id'].primary
|
406
|
+
assert CustomersView.columns_hash['id'].is_identity?
|
407
|
+
end
|
408
|
+
|
409
|
+
should 'find default values' do
|
410
|
+
assert_equal 0, CustomersView.new.balance
|
411
|
+
end
|
412
|
+
|
413
|
+
should 'respond true to table_exists?' do
|
414
|
+
assert CustomersView.table_exists?
|
415
|
+
end
|
416
|
+
|
417
|
+
should 'have correct table name for all column objects' do
|
418
|
+
assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' },
|
419
|
+
CustomersView.columns.map(&:table_name).inspect
|
420
|
+
end
|
421
|
+
|
398
422
|
end
|
399
423
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
424
|
+
context 'with aliased column names' do
|
425
|
+
|
426
|
+
should 'have matching column objects' do
|
427
|
+
columns = ['id','pretend_null']
|
428
|
+
assert !StringDefaultsView.columns.blank?
|
429
|
+
assert_equal columns.size, StringDefaultsView.columns.size
|
430
|
+
columns.each do |colname|
|
431
|
+
assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
|
432
|
+
StringDefaultsView.columns_hash[colname],
|
433
|
+
"Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}"
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
should 'find identity column' do
|
438
|
+
assert StringDefaultsView.columns_hash['id'].primary
|
439
|
+
assert StringDefaultsView.columns_hash['id'].is_identity?
|
440
|
+
end
|
441
|
+
|
442
|
+
should 'find default values' do
|
443
|
+
# col_default_sql = "SELECT c.COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = 'string_defaults' AND c.COLUMN_NAME = 'string_with_pretend_null_one'"
|
444
|
+
# raise @connection.select_value(col_default_sql).inspect
|
445
|
+
# raise @connection.without_type_conversion{ @connection.select_value(col_default_sql) }.inspect
|
446
|
+
|
447
|
+
assert_equal 'null', StringDefaultsView.new.pretend_null,
|
448
|
+
StringDefaultsView.columns_hash['pretend_null'].inspect
|
449
|
+
end
|
450
|
+
|
451
|
+
should 'respond true to table_exists?' do
|
452
|
+
assert StringDefaultsView.table_exists?
|
453
|
+
end
|
454
|
+
|
455
|
+
should 'have correct table name for all column objects' do
|
456
|
+
assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' },
|
457
|
+
StringDefaultsView.columns.map(&:table_name).inspect
|
458
|
+
end
|
459
|
+
|
417
460
|
end
|
418
461
|
|
419
462
|
end
|
@@ -41,6 +41,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
|
|
41
41
|
|
42
42
|
setup do
|
43
43
|
@select_sql = 'SELECT * FROM books'
|
44
|
+
@subquery_select_sql = 'SELECT *, (SELECT TOP 1 id FROM books) AS book_id FROM books'
|
44
45
|
@books = (1..10).map {|i| Book.create!}
|
45
46
|
end
|
46
47
|
|
@@ -75,6 +76,12 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
|
|
75
76
|
assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
|
76
77
|
end
|
77
78
|
|
79
|
+
should 'not create invalid SQL with subquery SELECTs with TOP' do
|
80
|
+
options = { :limit => 5, :offset => 1 }
|
81
|
+
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"
|
82
|
+
assert_equal expected_sql, @connection.add_limit_offset!(@subquery_select_sql,options)
|
83
|
+
end
|
84
|
+
|
78
85
|
end
|
79
86
|
|
80
87
|
|
@@ -70,7 +70,7 @@ ActiveRecord::Schema.define do
|
|
70
70
|
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_view') DROP VIEW string_defaults_view"
|
71
71
|
execute <<-STRINGDEFAULTSVIEW
|
72
72
|
CREATE VIEW string_defaults_view AS
|
73
|
-
SELECT id, string_with_pretend_null_one
|
73
|
+
SELECT id, string_with_pretend_null_one as pretend_null
|
74
74
|
FROM string_defaults
|
75
75
|
STRINGDEFAULTSVIEW
|
76
76
|
|