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 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
- * Support for views as table names. Also support views during identity inserts.
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 thoughbot-shoulda
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.2'.freeze
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.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally").to_i
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
- real_table_name = table_name_or_views_table_name(table_name)
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
- CASE
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 = '#{real_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] = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/)[1] if 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
- assert_nothing_raised(ActiveRecord::StatementInvalid) do
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
- should 'yield column objects' do
393
- assert !CustomersView.columns.blank?
394
- ['id','name','balance'].each do |colname|
395
- assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
396
- CustomersView.columns_hash[colname], "Column name #{colname.inspect} was not found"
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
- should 'find identity column' do
401
- assert CustomersView.columns_hash['id'].primary
402
- assert CustomersView.columns_hash['id'].is_identity?
403
- end
404
-
405
- should 'pick up on default values from table' do
406
- assert_equal 0, CustomersView.new.balance
407
- assert_equal 'null', StringDefaultsView.new.string_with_pretend_null_one
408
- end
409
-
410
- should 'yield all column objects having the correct table name' do
411
- assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' },
412
- CustomersView.columns.map(&:table_name).inspect
413
- end
414
-
415
- should 'respond true to table_exists?' do
416
- assert CustomersView.table_exists?
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-sqlserver-2000-2005-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins