Skip to content

Commit 7a18ff1

Browse files
committed
Initial 3.2 compatability. All tests green.
* Make use of the new ConnectionAdapters::SchemaCache for our needs. * New Sqlserver::Utils class for out helpers. Moved table name unquotes there.
1 parent 55aa57f commit 7a18ff1

File tree

13 files changed

+227
-107
lines changed

13 files changed

+227
-107
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11

2+
* 3.2.0 *
3+
4+
*
5+
6+
27
* 3.1.5 *
38

49
* Better support for orders with an expression. Fixes #155. [Jason Frey, Joe Rafaniello]

lib/active_record/connection_adapters/sqlserver/quoting.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ def quote_string(string)
4242
end
4343

4444
def quote_column_name(name)
45-
@sqlserver_quoted_column_and_table_names[name] ||=
46-
name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
45+
schema_cache.quote_name(name)
4746
end
4847

4948
def quote_table_name(name)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
module ActiveRecord
2+
module ConnectionAdapters
3+
module Sqlserver
4+
class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache
5+
6+
attr_reader :view_information
7+
8+
def initialize(conn)
9+
super
10+
@table_names = nil
11+
@view_names = nil
12+
@view_information = {}
13+
@quoted_names = {}
14+
end
15+
16+
# Superclass Overrides
17+
18+
def table_exists?(table_name)
19+
return false if table_name.blank?
20+
key = table_name_key(table_name)
21+
return @tables[key] if @tables.key? key
22+
@tables[key] = connection.table_exists?(table_name)
23+
end
24+
25+
def clear!
26+
super
27+
@table_names = nil
28+
@view_names = nil
29+
@view_information.clear
30+
@quoted_names.clear
31+
end
32+
33+
def clear_table_cache!(table_name)
34+
key = table_name_key(table_name)
35+
super(key)
36+
super(table_name)
37+
# SQL Server Specific
38+
if @table_names
39+
@table_names.delete key
40+
@table_names.delete table_name
41+
end
42+
if @view_names
43+
@view_names.delete key
44+
@view_names.delete table_name
45+
end
46+
@view_information.delete key
47+
end
48+
49+
# SQL Server Specific
50+
51+
def table_names
52+
@table_names ||= connection.tables
53+
end
54+
55+
def view_names
56+
@view_names ||= connection.views
57+
end
58+
59+
def view_exists?(table_name)
60+
table_exists?(table_name)
61+
end
62+
63+
def view_information(table_name)
64+
key = table_name_key(table_name)
65+
return @view_information[key] if @view_information.key? key
66+
@view_information[key] = connection.send(:view_information, table_name)
67+
end
68+
69+
def quote_name(name)
70+
return @quoted_names[name] if @quoted_names.key? name
71+
@quoted_names[name] = name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
72+
end
73+
74+
75+
private
76+
77+
def table_name_key(table_name)
78+
Utils.unqualify_table_name(table_name)
79+
end
80+
81+
end
82+
end
83+
end
84+
end
85+

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

Lines changed: 31 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ def native_database_types
77
@native_database_types ||= initialize_native_database_types.freeze
88
end
99

10-
def tables(name = nil, table_type = 'BASE TABLE')
10+
def tables(table_type = 'BASE TABLE')
1111
info_schema_query do
1212
select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME"
1313
end
1414
end
1515

1616
def table_exists?(table_name)
1717
return false if table_name.blank?
18-
unquoted_table_name = unqualify_table_name(table_name)
18+
unquoted_table_name = Utils.unqualify_table_name(table_name)
1919
super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name)
2020
end
2121

@@ -40,31 +40,16 @@ def indexes(table_name, name = nil)
4040

4141
def columns(table_name, name = nil)
4242
return [] if table_name.blank?
43-
@sqlserver_columns_cache[table_name] ||= column_definitions(table_name).collect do |ci|
43+
column_definitions(table_name).collect do |ci|
4444
sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
4545
SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
4646
end
4747
end
48-
49-
def create_table(table_name, options = {})
50-
super
51-
clear_cache!
52-
end
5348

5449
def rename_table(table_name, new_name)
5550
do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
5651
end
5752

58-
def drop_table(table_name, options = {})
59-
super
60-
clear_cache!
61-
end
62-
63-
def add_column(table_name, column_name, type, options = {})
64-
super
65-
clear_cache!
66-
end
67-
6853
def remove_column(table_name, *column_names)
6954
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
7055
column_names.flatten.each do |column_name|
@@ -73,12 +58,11 @@ def remove_column(table_name, *column_names)
7358
remove_indexes(table_name, column_name)
7459
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
7560
end
76-
clear_cache!
7761
end
7862

7963
def change_column(table_name, column_name, type, options = {})
8064
sql_commands = []
81-
column_object = columns(table_name).detect { |c| c.name.to_s == column_name.to_s }
65+
column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s }
8266
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
8367
change_column_sql << " NOT NULL" if options[:null] == false
8468
sql_commands << change_column_sql
@@ -89,19 +73,16 @@ def change_column(table_name, column_name, type, options = {})
8973
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}"
9074
end
9175
sql_commands.each { |c| do_execute(c) }
92-
clear_cache!
9376
end
9477

9578
def change_column_default(table_name, column_name, default)
9679
remove_default_constraint(table_name, column_name)
9780
do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
98-
clear_cache!
9981
end
10082

10183
def rename_column(table_name, column_name, new_column_name)
102-
detect_column_for!(table_name,column_name)
84+
detect_column_for! table_name, column_name
10385
do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
104-
clear_cache!
10586
end
10687

10788
def remove_index!(table_name, index_name)
@@ -125,7 +106,7 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
125106
end
126107

127108
def change_column_null(table_name, column_name, null, default = nil)
128-
column = detect_column_for!(table_name,column_name)
109+
column = detect_column_for! table_name, column_name
129110
unless null || default.nil?
130111
do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
131112
end
@@ -136,8 +117,8 @@ def change_column_null(table_name, column_name, null, default = nil)
136117

137118
# === SQLServer Specific ======================================== #
138119

139-
def views(name = nil)
140-
@sqlserver_views_cache ||= tables(name,'VIEW')
120+
def views
121+
tables('VIEW')
141122
end
142123

143124

@@ -171,10 +152,10 @@ def initialize_native_database_types
171152
end
172153

173154
def column_definitions(table_name)
174-
db_name = unqualify_db_name(table_name)
155+
db_name = Utils.unqualify_db_name(table_name)
175156
db_name_with_period = "#{db_name}." if db_name
176-
table_schema = unqualify_table_schema(table_name)
177-
table_name = unqualify_table_name(table_name)
157+
table_schema = Utils.unqualify_table_schema(table_name)
158+
table_name = Utils.unqualify_table_name(table_name)
178159
sql = %{
179160
SELECT DISTINCT
180161
#{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
@@ -230,7 +211,7 @@ def column_definitions(table_name)
230211
else
231212
ci[:type]
232213
end
233-
if ci[:default_value].nil? && views.include?(table_name)
214+
if ci[:default_value].nil? && schema_cache.view_names.include?(table_name)
234215
real_table_name = table_name_or_views_table_name(table_name)
235216
real_column_name = views_real_column_name(table_name,ci[:name])
236217
col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
@@ -281,19 +262,6 @@ def info_schema_query
281262
log_info_schema_queries ? yield : ActiveRecord::Base.silence{ yield }
282263
end
283264

284-
def unqualify_table_name(table_name)
285-
table_name.to_s.split('.').last.tr('[]','')
286-
end
287-
288-
def unqualify_table_schema(table_name)
289-
table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
290-
end
291-
292-
def unqualify_db_name(table_name)
293-
table_names = table_name.to_s.split('.')
294-
table_names.length == 3 ? table_names.first.tr('[]','') : nil
295-
end
296-
297265
def get_table_name(sql)
298266
if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
299267
$2 || $3
@@ -309,7 +277,7 @@ def default_constraint_name(table_name, column_name)
309277
end
310278

311279
def detect_column_for!(table_name, column_name)
312-
unless column = columns(table_name).detect { |c| c.name == column_name.to_s }
280+
unless column = schema_cache.columns[table_name].detect { |c| c.name == column_name.to_s }
313281
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
314282
end
315283
column
@@ -322,50 +290,40 @@ def lowercase_schema_reflection_sql(node)
322290
# === SQLServer Specific (View Reflection) ====================== #
323291

324292
def view_table_name(table_name)
325-
view_info = view_information(table_name)
293+
view_info = schema_cache.view_information(table_name)
326294
view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
327295
end
328296

329297
def view_information(table_name)
330-
table_name = unqualify_table_name(table_name)
331-
@sqlserver_view_information_cache[table_name] ||= begin
332-
view_info = info_schema_query { select_one("SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'") }
333-
if view_info
334-
view_info = view_info.with_indifferent_access
335-
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
336-
view_info[:VIEW_DEFINITION] = info_schema_query do
337-
begin
338-
select_values("EXEC sp_helptext #{quote_table_name(table_name)}").join
339-
rescue
340-
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
341-
end
298+
table_name = Utils.unqualify_table_name(table_name)
299+
view_info = info_schema_query { select_one("SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'") }
300+
if view_info
301+
view_info = view_info.with_indifferent_access
302+
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
303+
view_info[:VIEW_DEFINITION] = info_schema_query do
304+
begin
305+
select_values("EXEC sp_helptext #{quote_table_name(table_name)}").join
306+
rescue
307+
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
308+
nil
342309
end
343-
end
310+
end
344311
end
345-
view_info
346312
end
313+
view_info
347314
end
348315

349316
def table_name_or_views_table_name(table_name)
350-
unquoted_table_name = unqualify_table_name(table_name)
351-
views.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
317+
unquoted_table_name = Utils.unqualify_table_name(table_name)
318+
schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
352319
end
353320

354321
def views_real_column_name(table_name,column_name)
355-
view_definition = view_information(table_name)[:VIEW_DEFINITION]
322+
view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION]
356323
match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
357324
match_data ? match_data[1] : column_name
358325
end
359326

360-
# === SQLServer Specific (Column/View Caches) =================== #
361-
362-
def initialize_sqlserver_caches
363-
@sqlserver_columns_cache = {}
364-
@sqlserver_views_cache = nil
365-
@sqlserver_view_information_cache = {}
366-
@sqlserver_quoted_column_and_table_names = {}
367-
end
368-
369327
# === SQLServer Specific (Identity Inserts) ===================== #
370328

371329
def query_requires_identity_insert?(sql)
@@ -398,7 +356,7 @@ def set_identity_insert(table_name, enable = true)
398356
end
399357

400358
def identity_column(table_name)
401-
columns(table_name).detect(&:is_identity?)
359+
schema_cache.columns[table_name].detect(&:is_identity?)
402360
end
403361

404362
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module ActiveRecord
2+
module ConnectionAdapters
3+
module Sqlserver
4+
class Utils
5+
6+
class << self
7+
8+
def unqualify_table_name(table_name)
9+
table_name.to_s.split('.').last.tr('[]','')
10+
end
11+
12+
def unqualify_table_schema(table_name)
13+
table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
14+
end
15+
16+
def unqualify_db_name(table_name)
17+
table_names = table_name.to_s.split('.')
18+
table_names.length == 3 ? table_names.first.tr('[]','') : nil
19+
end
20+
21+
end
22+
23+
end
24+
end
25+
end
26+
end
27+
28+

lib/active_record/connection_adapters/sqlserver/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module ConnectionAdapters
33
module Sqlserver
44
module Version
55

6-
VERSION = '3.1.5'
6+
VERSION = '3.2.0.rc1'
77

88
end
99
end

0 commit comments

Comments
 (0)