activerecord-sqlserver-adapter 3.1.7 → 3.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -11
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +41 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +31 -29
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +36 -86
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +67 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +69 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +39 -30
- data/lib/arel/visitors/sqlserver.rb +2 -3
- metadata +63 -27
data/CHANGELOG
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
|
2
|
-
* 3.
|
2
|
+
* 3.2.0 *
|
3
3
|
|
4
|
-
*
|
4
|
+
* ActiveRecord explain (SHOWPLAN) support.
|
5
|
+
http://youtu.be/ckb3YYZZZ2Q
|
5
6
|
|
6
|
-
*
|
7
|
+
* Remove our log_info_schema_queries config since we are not hooking properly into AR's 'SCHEMA' names.
|
7
8
|
|
8
|
-
*
|
9
|
+
* Properly use 'SCHEMA' name arguement in DB statements to comply with ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS.
|
9
10
|
|
10
|
-
*
|
11
|
+
* Make use of the new ConnectionAdapters::SchemaCache for our needs.
|
11
12
|
|
12
|
-
*
|
13
|
-
|
14
|
-
|
15
|
-
* 3.1.6 *
|
16
|
-
|
17
|
-
* Add explicit order-by clause for windowed results. Fixes #161.
|
13
|
+
* New Sqlserver::Utils class for out helpers. Moved table name unquotes there.
|
18
14
|
|
19
15
|
|
20
16
|
* 3.1.5 *
|
@@ -1,9 +1,3 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'active_record/base'
|
3
|
-
require 'active_record/version'
|
4
|
-
require 'active_support/concern'
|
5
|
-
require 'active_support/core_ext/class/attribute'
|
6
|
-
|
7
1
|
module ActiveRecord
|
8
2
|
module ConnectionAdapters
|
9
3
|
module Sqlserver
|
@@ -46,4 +40,3 @@ end
|
|
46
40
|
|
47
41
|
|
48
42
|
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord
|
49
|
-
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
module CoreExt
|
5
|
+
module Explain
|
6
|
+
|
7
|
+
SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql "
|
8
|
+
SQLSERVER_PARAM_MATCHER = /@\d+ =/
|
9
|
+
|
10
|
+
def exec_explain(queries)
|
11
|
+
unprepared_queries = queries.map { |sql, bind| [unprepare_sqlserver_statement(sql), bind] }
|
12
|
+
super(unprepared_queries)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# This is somewhat hacky, but it should reliably reformat our prepared sql statment
|
18
|
+
# which uses sp_executesql to just the first argument, then unquote it. Likewise our
|
19
|
+
# do_exec_query method should substitude the @n args withe the quoted values.
|
20
|
+
def unprepare_sqlserver_statement(sql)
|
21
|
+
if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX)
|
22
|
+
executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length)
|
23
|
+
executesql_args = executesql.split(', ')
|
24
|
+
executesql_args.reject! { |arg| arg =~ SQLSERVER_PARAM_MATCHER }
|
25
|
+
executesql_args.pop if executesql_args.many?
|
26
|
+
executesql = executesql_args.join(', ').strip.match(/N'(.*)'/)[1]
|
27
|
+
Utils.unquote_string(executesql)
|
28
|
+
else
|
29
|
+
sql
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain
|
41
|
+
ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain
|
@@ -40,7 +40,7 @@ module ActiveRecord
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def outside_transaction?
|
43
|
-
|
43
|
+
select_value('SELECT @@TRANCOUNT', 'SCHEMA') == 0
|
44
44
|
end
|
45
45
|
|
46
46
|
def supports_statement_cache?
|
@@ -131,19 +131,17 @@ module ActiveRecord
|
|
131
131
|
|
132
132
|
def user_options
|
133
133
|
return {} if sqlserver_azure?
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
values
|
140
|
-
end
|
134
|
+
select_rows("dbcc useroptions",'SCHEMA').inject(HashWithIndifferentAccess.new) do |values,row|
|
135
|
+
set_option = row[0].gsub(/\s+/,'_')
|
136
|
+
user_value = row[1]
|
137
|
+
values[set_option] = user_value
|
138
|
+
values
|
141
139
|
end
|
142
140
|
end
|
143
141
|
|
144
142
|
def user_options_dateformat
|
145
143
|
if sqlserver_azure?
|
146
|
-
|
144
|
+
select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
|
147
145
|
else
|
148
146
|
user_options['dateformat']
|
149
147
|
end
|
@@ -151,18 +149,16 @@ module ActiveRecord
|
|
151
149
|
|
152
150
|
def user_options_isolation_level
|
153
151
|
if sqlserver_azure?
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
select_value(sql)
|
165
|
-
end
|
152
|
+
sql = %|SELECT CASE [transaction_isolation_level]
|
153
|
+
WHEN 0 THEN NULL
|
154
|
+
WHEN 1 THEN 'READ UNCOMITTED'
|
155
|
+
WHEN 2 THEN 'READ COMITTED'
|
156
|
+
WHEN 3 THEN 'REPEATABLE'
|
157
|
+
WHEN 4 THEN 'SERIALIZABLE'
|
158
|
+
WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
|
159
|
+
FROM [sys].[dm_exec_sessions]
|
160
|
+
WHERE [session_id] = @@SPID|.squish
|
161
|
+
select_value sql, 'SCHEMA'
|
166
162
|
else
|
167
163
|
user_options['isolation_level']
|
168
164
|
end
|
@@ -170,7 +166,7 @@ module ActiveRecord
|
|
170
166
|
|
171
167
|
def user_options_language
|
172
168
|
if sqlserver_azure?
|
173
|
-
|
169
|
+
select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA'
|
174
170
|
else
|
175
171
|
user_options['language']
|
176
172
|
end
|
@@ -314,15 +310,14 @@ module ActiveRecord
|
|
314
310
|
|
315
311
|
# === SQLServer Specific (Executing) ============================ #
|
316
312
|
|
317
|
-
def do_execute(sql, name =
|
318
|
-
name ||= 'EXECUTE'
|
313
|
+
def do_execute(sql, name = 'SQL')
|
319
314
|
log(sql, name) do
|
320
315
|
with_sqlserver_error_handling { raw_connection_do(sql) }
|
321
316
|
end
|
322
317
|
end
|
323
318
|
|
324
319
|
def do_exec_query(sql, name, binds)
|
325
|
-
|
320
|
+
explaining = name == 'EXPLAIN'
|
326
321
|
names_and_types = []
|
327
322
|
params = []
|
328
323
|
binds.each_with_index do |(column,value),index|
|
@@ -341,10 +336,17 @@ module ActiveRecord
|
|
341
336
|
raise "Unknown bind columns. We can account for this."
|
342
337
|
end
|
343
338
|
quoted_value = ar_column ? quote(v,column) : quote(v,nil)
|
344
|
-
params << "@#{index} = #{quoted_value}"
|
339
|
+
params << (explaining ? quoted_value : "@#{index} = #{quoted_value}")
|
340
|
+
end
|
341
|
+
if explaining
|
342
|
+
params.each_with_index do |param, index|
|
343
|
+
substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
|
344
|
+
sql.sub! substitute_at_finder, param
|
345
|
+
end
|
346
|
+
else
|
347
|
+
sql = "EXEC sp_executesql #{quote(sql)}"
|
348
|
+
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
345
349
|
end
|
346
|
-
sql = "EXEC sp_executesql #{statement}"
|
347
|
-
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
348
350
|
raw_select sql, name, binds, :ar_result => true
|
349
351
|
end
|
350
352
|
|
@@ -361,7 +363,7 @@ module ActiveRecord
|
|
361
363
|
|
362
364
|
# === SQLServer Specific (Selecting) ============================ #
|
363
365
|
|
364
|
-
def raw_select(sql, name=
|
366
|
+
def raw_select(sql, name='SQL', binds=[], options={})
|
365
367
|
log(sql,name,binds) { _raw_select(sql, options) }
|
366
368
|
end
|
367
369
|
|
@@ -42,8 +42,7 @@ module ActiveRecord
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def quote_column_name(name)
|
45
|
-
|
46
|
-
name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
|
45
|
+
schema_cache.quote_name(name)
|
47
46
|
end
|
48
47
|
|
49
48
|
def quote_table_name(name)
|
@@ -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
|
+
|
@@ -7,15 +7,13 @@ module ActiveRecord
|
|
7
7
|
@native_database_types ||= initialize_native_database_types.freeze
|
8
8
|
end
|
9
9
|
|
10
|
-
def tables(
|
11
|
-
|
12
|
-
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"
|
13
|
-
end
|
10
|
+
def tables(table_type = 'BASE TABLE')
|
11
|
+
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", 'SCHEMA'
|
14
12
|
end
|
15
13
|
|
16
14
|
def table_exists?(table_name)
|
17
15
|
return false if table_name.blank?
|
18
|
-
unquoted_table_name = unqualify_table_name(table_name)
|
16
|
+
unquoted_table_name = Utils.unqualify_table_name(table_name)
|
19
17
|
super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name)
|
20
18
|
end
|
21
19
|
|
@@ -40,31 +38,16 @@ module ActiveRecord
|
|
40
38
|
|
41
39
|
def columns(table_name, name = nil)
|
42
40
|
return [] if table_name.blank?
|
43
|
-
|
41
|
+
column_definitions(table_name).collect do |ci|
|
44
42
|
sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
|
45
43
|
SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
|
46
44
|
end
|
47
45
|
end
|
48
|
-
|
49
|
-
def create_table(table_name, options = {})
|
50
|
-
super
|
51
|
-
clear_cache!
|
52
|
-
end
|
53
46
|
|
54
47
|
def rename_table(table_name, new_name)
|
55
48
|
do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
|
56
49
|
end
|
57
50
|
|
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
|
-
|
68
51
|
def remove_column(table_name, *column_names)
|
69
52
|
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
70
53
|
column_names.flatten.each do |column_name|
|
@@ -73,12 +56,11 @@ module ActiveRecord
|
|
73
56
|
remove_indexes(table_name, column_name)
|
74
57
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
75
58
|
end
|
76
|
-
clear_cache!
|
77
59
|
end
|
78
60
|
|
79
61
|
def change_column(table_name, column_name, type, options = {})
|
80
62
|
sql_commands = []
|
81
|
-
column_object = columns
|
63
|
+
column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s }
|
82
64
|
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])}"
|
83
65
|
change_column_sql << " NOT NULL" if options[:null] == false
|
84
66
|
sql_commands << change_column_sql
|
@@ -89,19 +71,16 @@ module ActiveRecord
|
|
89
71
|
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)}"
|
90
72
|
end
|
91
73
|
sql_commands.each { |c| do_execute(c) }
|
92
|
-
clear_cache!
|
93
74
|
end
|
94
75
|
|
95
76
|
def change_column_default(table_name, column_name, default)
|
96
77
|
remove_default_constraint(table_name, column_name)
|
97
78
|
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!
|
99
79
|
end
|
100
80
|
|
101
81
|
def rename_column(table_name, column_name, new_column_name)
|
102
|
-
detect_column_for!
|
82
|
+
detect_column_for! table_name, column_name
|
103
83
|
do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
|
104
|
-
clear_cache!
|
105
84
|
end
|
106
85
|
|
107
86
|
def remove_index!(table_name, index_name)
|
@@ -125,7 +104,7 @@ module ActiveRecord
|
|
125
104
|
end
|
126
105
|
|
127
106
|
def change_column_null(table_name, column_name, null, default = nil)
|
128
|
-
column = detect_column_for!
|
107
|
+
column = detect_column_for! table_name, column_name
|
129
108
|
unless null || default.nil?
|
130
109
|
do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
131
110
|
end
|
@@ -136,8 +115,8 @@ module ActiveRecord
|
|
136
115
|
|
137
116
|
# === SQLServer Specific ======================================== #
|
138
117
|
|
139
|
-
def views
|
140
|
-
|
118
|
+
def views
|
119
|
+
tables('VIEW')
|
141
120
|
end
|
142
121
|
|
143
122
|
|
@@ -171,10 +150,10 @@ module ActiveRecord
|
|
171
150
|
end
|
172
151
|
|
173
152
|
def column_definitions(table_name)
|
174
|
-
db_name = unqualify_db_name(table_name)
|
153
|
+
db_name = Utils.unqualify_db_name(table_name)
|
175
154
|
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)
|
155
|
+
table_schema = Utils.unqualify_table_schema(table_name)
|
156
|
+
table_name = Utils.unqualify_table_name(table_name)
|
178
157
|
sql = %{
|
179
158
|
SELECT DISTINCT
|
180
159
|
#{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
|
@@ -215,7 +194,7 @@ module ActiveRecord
|
|
215
194
|
}.gsub(/[ \t\r\n]+/,' ')
|
216
195
|
binds = [['table_name', table_name]]
|
217
196
|
binds << ['table_schema',table_schema] unless table_schema.blank?
|
218
|
-
results =
|
197
|
+
results = do_exec_query(sql, 'SCHEMA', binds)
|
219
198
|
results.collect do |ci|
|
220
199
|
ci = ci.symbolize_keys
|
221
200
|
ci[:type] = case ci[:type]
|
@@ -230,11 +209,11 @@ module ActiveRecord
|
|
230
209
|
else
|
231
210
|
ci[:type]
|
232
211
|
end
|
233
|
-
if ci[:default_value].nil? &&
|
212
|
+
if ci[:default_value].nil? && schema_cache.view_names.include?(table_name)
|
234
213
|
real_table_name = table_name_or_views_table_name(table_name)
|
235
214
|
real_column_name = views_real_column_name(table_name,ci[:name])
|
236
215
|
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}'"
|
237
|
-
ci[:default_value] =
|
216
|
+
ci[:default_value] = select_value col_default_sql, 'SCHEMA'
|
238
217
|
end
|
239
218
|
ci[:default_value] = case ci[:default_value]
|
240
219
|
when nil, '(null)', '(NULL)'
|
@@ -254,7 +233,7 @@ module ActiveRecord
|
|
254
233
|
end
|
255
234
|
|
256
235
|
def remove_check_constraints(table_name, column_name)
|
257
|
-
constraints =
|
236
|
+
constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA'
|
258
237
|
constraints.each do |constraint|
|
259
238
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
|
260
239
|
end
|
@@ -277,23 +256,6 @@ module ActiveRecord
|
|
277
256
|
|
278
257
|
# === SQLServer Specific (Misc Helpers) ========================= #
|
279
258
|
|
280
|
-
def info_schema_query
|
281
|
-
log_info_schema_queries ? yield : ActiveRecord::Base.silence{ yield }
|
282
|
-
end
|
283
|
-
|
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
|
-
|
297
259
|
def get_table_name(sql)
|
298
260
|
if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
299
261
|
$2 || $3
|
@@ -309,7 +271,7 @@ module ActiveRecord
|
|
309
271
|
end
|
310
272
|
|
311
273
|
def detect_column_for!(table_name, column_name)
|
312
|
-
unless column = columns
|
274
|
+
unless column = schema_cache.columns[table_name].detect { |c| c.name == column_name.to_s }
|
313
275
|
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
314
276
|
end
|
315
277
|
column
|
@@ -322,57 +284,45 @@ module ActiveRecord
|
|
322
284
|
# === SQLServer Specific (View Reflection) ====================== #
|
323
285
|
|
324
286
|
def view_table_name(table_name)
|
325
|
-
view_info = view_information(table_name)
|
287
|
+
view_info = schema_cache.view_information(table_name)
|
326
288
|
view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
|
327
289
|
end
|
328
290
|
|
329
291
|
def view_information(table_name)
|
330
|
-
table_name = unqualify_table_name(table_name)
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|
292
|
+
table_name = Utils.unqualify_table_name(table_name)
|
293
|
+
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'", 'SCHEMA'
|
294
|
+
if view_info
|
295
|
+
view_info = view_info.with_indifferent_access
|
296
|
+
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
|
297
|
+
view_info[:VIEW_DEFINITION] = begin
|
298
|
+
select_values("EXEC sp_helptext #{quote_table_name(table_name)}", 'SCHEMA').join
|
299
|
+
rescue
|
300
|
+
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
|
301
|
+
nil
|
302
|
+
end
|
344
303
|
end
|
345
|
-
view_info
|
346
304
|
end
|
305
|
+
view_info
|
347
306
|
end
|
348
307
|
|
349
308
|
def table_name_or_views_table_name(table_name)
|
350
|
-
unquoted_table_name = unqualify_table_name(table_name)
|
351
|
-
|
309
|
+
unquoted_table_name = Utils.unqualify_table_name(table_name)
|
310
|
+
schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
|
352
311
|
end
|
353
312
|
|
354
313
|
def views_real_column_name(table_name,column_name)
|
355
|
-
view_definition = view_information(table_name)[:VIEW_DEFINITION]
|
314
|
+
view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION]
|
356
315
|
match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
|
357
316
|
match_data ? match_data[1] : column_name
|
358
317
|
end
|
359
318
|
|
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
|
-
|
369
319
|
# === SQLServer Specific (Identity Inserts) ===================== #
|
370
320
|
|
371
321
|
def query_requires_identity_insert?(sql)
|
372
322
|
if insert_sql?(sql)
|
373
323
|
table_name = get_table_name(sql)
|
374
324
|
id_column = identity_column(table_name)
|
375
|
-
id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)
|
325
|
+
id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
|
376
326
|
else
|
377
327
|
false
|
378
328
|
end
|
@@ -392,13 +342,13 @@ module ActiveRecord
|
|
392
342
|
|
393
343
|
def set_identity_insert(table_name, enable = true)
|
394
344
|
sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
395
|
-
do_execute sql,'
|
345
|
+
do_execute sql, 'SCHEMA'
|
396
346
|
rescue Exception => e
|
397
347
|
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
398
348
|
end
|
399
349
|
|
400
350
|
def identity_column(table_name)
|
401
|
-
columns
|
351
|
+
schema_cache.columns[table_name].detect(&:is_identity?)
|
402
352
|
end
|
403
353
|
|
404
354
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'active_record/connection_adapters/sqlserver/showplan/printer_table'
|
2
|
+
require 'active_record/connection_adapters/sqlserver/showplan/printer_xml'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
module Sqlserver
|
7
|
+
module Showplan
|
8
|
+
|
9
|
+
OPTION_ALL = 'SHOWPLAN_ALL'
|
10
|
+
OPTION_TEXT = 'SHOWPLAN_TEXT'
|
11
|
+
OPTION_XML = 'SHOWPLAN_XML'
|
12
|
+
OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML]
|
13
|
+
|
14
|
+
def explain(arel, binds = [])
|
15
|
+
sql = to_sql(arel)
|
16
|
+
result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) }
|
17
|
+
printer = showplan_printer.new(result)
|
18
|
+
printer.pp
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def with_showplan_on
|
25
|
+
set_showplan_option(true)
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
set_showplan_option(false)
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_showplan_option(enable = true)
|
32
|
+
sql = "SET #{option} #{enable ? 'ON' : 'OFF'}"
|
33
|
+
raw_connection_do(sql)
|
34
|
+
rescue Exception => e
|
35
|
+
raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?"
|
36
|
+
end
|
37
|
+
|
38
|
+
def option
|
39
|
+
(SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt|
|
40
|
+
raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def showplan_all?
|
45
|
+
option == OPTION_ALL
|
46
|
+
end
|
47
|
+
|
48
|
+
def showplan_text?
|
49
|
+
option == OPTION_TEXT
|
50
|
+
end
|
51
|
+
|
52
|
+
def showplan_xml?
|
53
|
+
option == OPTION_XML
|
54
|
+
end
|
55
|
+
|
56
|
+
def showplan_printer
|
57
|
+
case option
|
58
|
+
when OPTION_XML then PrinterXml
|
59
|
+
when OPTION_ALL, OPTION_TEXT then PrinterTable
|
60
|
+
else PrinterTable
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
module Showplan
|
5
|
+
class PrinterTable
|
6
|
+
|
7
|
+
cattr_accessor :max_column_width, :cell_padding
|
8
|
+
self.max_column_width = 50
|
9
|
+
self.cell_padding = 1
|
10
|
+
|
11
|
+
attr_reader :result
|
12
|
+
|
13
|
+
def initialize(result)
|
14
|
+
@result = result
|
15
|
+
end
|
16
|
+
|
17
|
+
def pp
|
18
|
+
@widths = compute_column_widths
|
19
|
+
@separator = build_separator
|
20
|
+
pp = []
|
21
|
+
pp << @separator
|
22
|
+
pp << build_cells(result.columns)
|
23
|
+
pp << @separator
|
24
|
+
result.rows.each do |row|
|
25
|
+
pp << build_cells(row)
|
26
|
+
end
|
27
|
+
pp << @separator
|
28
|
+
pp.join("\n") + "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def compute_column_widths
|
34
|
+
[].tap do |computed_widths|
|
35
|
+
result.columns.each_with_index do |column, i|
|
36
|
+
cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) }
|
37
|
+
computed_width = cells_in_column.map(&:length).max
|
38
|
+
final_width = computed_width > max_column_width ? max_column_width : computed_width
|
39
|
+
computed_widths << final_width
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_separator
|
45
|
+
'+' + @widths.map {|w| '-' * (w + (cell_padding*2))}.join('+') + '+'
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_cells(items)
|
49
|
+
cells = []
|
50
|
+
items.each_with_index do |item, i|
|
51
|
+
cells << cast_item(item).ljust(@widths[i])
|
52
|
+
end
|
53
|
+
"| #{cells.join(' | ')} |"
|
54
|
+
end
|
55
|
+
|
56
|
+
def cast_item(item)
|
57
|
+
case item
|
58
|
+
when NilClass then 'NULL'
|
59
|
+
when Float then item.to_s.to(9)
|
60
|
+
else item.to_s.truncate(max_column_width)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
module Showplan
|
5
|
+
class PrinterXml
|
6
|
+
|
7
|
+
def initialize(result)
|
8
|
+
@result = result
|
9
|
+
end
|
10
|
+
|
11
|
+
def pp
|
12
|
+
xml = @result.rows.first.first
|
13
|
+
if defined?(Nokogiri)
|
14
|
+
Nokogiri::XML(xml).to_xml :indent => 2, :encoding => 'UTF-8'
|
15
|
+
else
|
16
|
+
xml
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
class Utils
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def unquote_string(string)
|
9
|
+
string.to_s.gsub(/\'\'/, "'")
|
10
|
+
end
|
11
|
+
|
12
|
+
def unqualify_table_name(table_name)
|
13
|
+
table_name.to_s.split('.').last.tr('[]','')
|
14
|
+
end
|
15
|
+
|
16
|
+
def unqualify_table_schema(table_name)
|
17
|
+
table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def unqualify_db_name(table_name)
|
21
|
+
table_names = table_name.to_s.split('.')
|
22
|
+
table_names.length == 3 ? table_names.first.tr('[]','') : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
@@ -1,17 +1,22 @@
|
|
1
|
+
require 'base64'
|
1
2
|
require 'arel/visitors/sqlserver'
|
2
3
|
require 'active_record'
|
4
|
+
require 'active_record/base'
|
5
|
+
require 'active_support/concern'
|
6
|
+
require 'active_support/core_ext/string'
|
3
7
|
require 'active_record/connection_adapters/abstract_adapter'
|
4
8
|
require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
|
5
9
|
require 'active_record/connection_adapters/sqlserver/core_ext/database_statements'
|
10
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/explain'
|
6
11
|
require 'active_record/connection_adapters/sqlserver/database_limits'
|
7
12
|
require 'active_record/connection_adapters/sqlserver/database_statements'
|
8
13
|
require 'active_record/connection_adapters/sqlserver/errors'
|
14
|
+
require 'active_record/connection_adapters/sqlserver/schema_cache'
|
9
15
|
require 'active_record/connection_adapters/sqlserver/schema_statements'
|
16
|
+
require 'active_record/connection_adapters/sqlserver/showplan'
|
10
17
|
require 'active_record/connection_adapters/sqlserver/quoting'
|
18
|
+
require 'active_record/connection_adapters/sqlserver/utils'
|
11
19
|
require 'active_record/connection_adapters/sqlserver/version'
|
12
|
-
require 'active_support/core_ext/kernel/requires'
|
13
|
-
require 'active_support/core_ext/string'
|
14
|
-
require 'base64'
|
15
20
|
|
16
21
|
module ActiveRecord
|
17
22
|
|
@@ -32,7 +37,7 @@ module ActiveRecord
|
|
32
37
|
else
|
33
38
|
raise ArgumentError, "Unknown connection mode in #{config.inspect}."
|
34
39
|
end
|
35
|
-
ConnectionAdapters::SQLServerAdapter.new(logger,config.merge(:mode=>mode))
|
40
|
+
ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(:mode=>mode))
|
36
41
|
end
|
37
42
|
|
38
43
|
protected
|
@@ -91,7 +96,7 @@ module ActiveRecord
|
|
91
96
|
|
92
97
|
def sql_type_for_statement
|
93
98
|
if is_integer? || is_real?
|
94
|
-
sql_type.sub(/\(
|
99
|
+
sql_type.sub(/\(\d+\)/,'')
|
95
100
|
else
|
96
101
|
sql_type
|
97
102
|
end
|
@@ -168,6 +173,7 @@ module ActiveRecord
|
|
168
173
|
|
169
174
|
include Sqlserver::Quoting
|
170
175
|
include Sqlserver::DatabaseStatements
|
176
|
+
include Sqlserver::Showplan
|
171
177
|
include Sqlserver::SchemaStatements
|
172
178
|
include Sqlserver::DatabaseLimits
|
173
179
|
include Sqlserver::Errors
|
@@ -180,25 +186,22 @@ module ActiveRecord
|
|
180
186
|
attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition
|
181
187
|
|
182
188
|
cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
|
183
|
-
:
|
184
|
-
:cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration
|
189
|
+
:enable_default_unicode_types, :auto_connect, :retry_deadlock_victim,
|
190
|
+
:cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration,
|
191
|
+
:showplan_option
|
185
192
|
|
186
193
|
self.enable_default_unicode_types = true
|
187
194
|
|
188
|
-
class << self
|
189
|
-
|
190
|
-
def visitor_for(pool)
|
191
|
-
Arel::Visitors::SQLServer.new(pool)
|
192
|
-
end
|
193
|
-
|
194
|
-
end
|
195
195
|
|
196
|
-
def initialize(logger,config)
|
196
|
+
def initialize(connection, logger, pool, config)
|
197
|
+
super(connection, logger, pool)
|
198
|
+
# AbstractAdapter Responsibility
|
199
|
+
@schema_cache = Sqlserver::SchemaCache.new self
|
200
|
+
@visitor = Arel::Visitors::SQLServer.new self
|
201
|
+
# Our Responsibility
|
197
202
|
@connection_options = config
|
198
203
|
connect
|
199
|
-
|
200
|
-
@config = config
|
201
|
-
@database_version = info_schema_query { select_value('SELECT @@version') }
|
204
|
+
@database_version = select_value 'SELECT @@version', 'SCHEMA'
|
202
205
|
@database_year = begin
|
203
206
|
if @database_version =~ /Microsoft SQL Azure/i
|
204
207
|
@sqlserver_azure = true
|
@@ -210,11 +213,10 @@ module ActiveRecord
|
|
210
213
|
rescue
|
211
214
|
0
|
212
215
|
end
|
213
|
-
@product_level =
|
214
|
-
@product_version =
|
215
|
-
@edition =
|
216
|
+
@product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA'
|
217
|
+
@product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA'
|
218
|
+
@edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA'
|
216
219
|
initialize_dateformatter
|
217
|
-
initialize_sqlserver_caches
|
218
220
|
use_database
|
219
221
|
unless SUPPORTED_VERSIONS.include?(@database_year)
|
220
222
|
raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}."
|
@@ -243,10 +245,22 @@ module ActiveRecord
|
|
243
245
|
true
|
244
246
|
end
|
245
247
|
|
248
|
+
def supports_bulk_alter?
|
249
|
+
false
|
250
|
+
end
|
251
|
+
|
246
252
|
def supports_savepoints?
|
247
253
|
true
|
248
254
|
end
|
249
255
|
|
256
|
+
def supports_index_sort_order?
|
257
|
+
true
|
258
|
+
end
|
259
|
+
|
260
|
+
def supports_explain?
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
250
264
|
def disable_referential_integrity
|
251
265
|
do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
|
252
266
|
yield
|
@@ -287,10 +301,6 @@ module ActiveRecord
|
|
287
301
|
remove_database_connections_and_rollback { }
|
288
302
|
end
|
289
303
|
|
290
|
-
def clear_cache!
|
291
|
-
initialize_sqlserver_caches
|
292
|
-
end
|
293
|
-
|
294
304
|
# === Abstract Adapter (Misc Support) =========================== #
|
295
305
|
|
296
306
|
def pk_and_sequence_for(table_name)
|
@@ -299,7 +309,7 @@ module ActiveRecord
|
|
299
309
|
end
|
300
310
|
|
301
311
|
def primary_key(table_name)
|
302
|
-
identity_column(table_name).try(:name) || columns
|
312
|
+
identity_column(table_name).try(:name) || schema_cache.columns[table_name].detect(&:is_primary?).try(:name)
|
303
313
|
end
|
304
314
|
|
305
315
|
# === SQLServer Specific (DB Reflection) ======================== #
|
@@ -392,7 +402,7 @@ module ActiveRecord
|
|
392
402
|
|
393
403
|
def connect
|
394
404
|
config = @connection_options
|
395
|
-
@connection = case
|
405
|
+
@connection = case config[:mode]
|
396
406
|
when :dblib
|
397
407
|
appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
|
398
408
|
login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
@@ -424,7 +434,6 @@ module ActiveRecord
|
|
424
434
|
client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
|
425
435
|
client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
|
426
436
|
end
|
427
|
-
client.execute("SET TEXTSIZE 2147483647").do
|
428
437
|
end
|
429
438
|
when :odbc
|
430
439
|
if config[:dsn].include?(';')
|
@@ -444,7 +453,7 @@ module ActiveRecord
|
|
444
453
|
end
|
445
454
|
end
|
446
455
|
end
|
447
|
-
@spid
|
456
|
+
@spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first
|
448
457
|
configure_connection
|
449
458
|
rescue
|
450
459
|
raise unless @auto_connecting
|
@@ -154,8 +154,9 @@ module Arel
|
|
154
154
|
projections = projections.map { |x| projection_without_expression(x) }
|
155
155
|
end
|
156
156
|
[ ("SELECT" if !windowed),
|
157
|
+
(visit(core.set_quantifier) if core.set_quantifier),
|
157
158
|
(visit(o.limit) if o.limit && !windowed),
|
158
|
-
(projections.map{ |x|
|
159
|
+
(projections.map{ |x| visit(x) }.join(', ')),
|
159
160
|
(source_with_lock_for_select_statement(o)),
|
160
161
|
("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?),
|
161
162
|
("GROUP BY #{groups.map { |x| visit x }.join ', ' }" unless groups.empty?),
|
@@ -165,7 +166,6 @@ module Arel
|
|
165
166
|
end
|
166
167
|
|
167
168
|
def visit_Arel_Nodes_SelectStatementWithOffset(o)
|
168
|
-
o.limit ||= Arel::Nodes::Limit.new(9223372036854775807)
|
169
169
|
orders = rowtable_orders(o)
|
170
170
|
[ "SELECT",
|
171
171
|
(visit(o.limit) if o.limit && !windowed_single_distinct_select_statement?(o)),
|
@@ -175,7 +175,6 @@ module Arel
|
|
175
175
|
visit_Arel_Nodes_SelectStatementWithOutOffset(o,true),
|
176
176
|
") AS [__rnt]",
|
177
177
|
(visit(o.offset) if o.offset),
|
178
|
-
"ORDER BY [__rnt].[__rn] ASC"
|
179
178
|
].compact.join ' '
|
180
179
|
end
|
181
180
|
|
metadata
CHANGED
@@ -1,10 +1,17 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-sqlserver-adapter
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15424087
|
5
|
+
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 3
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
- rc
|
11
|
+
- 1
|
12
|
+
version: 3.2.0.rc1
|
6
13
|
platform: ruby
|
7
|
-
authors:
|
14
|
+
authors:
|
8
15
|
- Ken Collins
|
9
16
|
- Murray Steele
|
10
17
|
- Shawn Balestracci
|
@@ -13,61 +20,90 @@ authors:
|
|
13
20
|
autorequire:
|
14
21
|
bindir: bin
|
15
22
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
|
24
|
+
date: 2012-01-02 00:00:00 Z
|
25
|
+
dependencies:
|
26
|
+
- !ruby/object:Gem::Dependency
|
19
27
|
name: activerecord
|
20
|
-
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
21
30
|
none: false
|
22
|
-
requirements:
|
31
|
+
requirements:
|
23
32
|
- - ~>
|
24
|
-
- !ruby/object:Gem::Version
|
25
|
-
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
hash: 15424087
|
35
|
+
segments:
|
36
|
+
- 3
|
37
|
+
- 2
|
38
|
+
- 0
|
39
|
+
- rc
|
40
|
+
- 1
|
41
|
+
version: 3.2.0.rc1
|
26
42
|
type: :runtime
|
27
|
-
|
28
|
-
version_requirements: *70306769696760
|
43
|
+
version_requirements: *id001
|
29
44
|
description: SQL Server 2005 and 2008 Adapter For ActiveRecord
|
30
45
|
email: ken@metaskills.net
|
31
46
|
executables: []
|
47
|
+
|
32
48
|
extensions: []
|
49
|
+
|
33
50
|
extra_rdoc_files: []
|
34
|
-
|
51
|
+
|
52
|
+
files:
|
35
53
|
- CHANGELOG
|
36
54
|
- MIT-LICENSE
|
37
55
|
- lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb
|
38
56
|
- lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb
|
57
|
+
- lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb
|
39
58
|
- lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb
|
40
59
|
- lib/active_record/connection_adapters/sqlserver/database_limits.rb
|
41
60
|
- lib/active_record/connection_adapters/sqlserver/database_statements.rb
|
42
61
|
- lib/active_record/connection_adapters/sqlserver/errors.rb
|
43
62
|
- lib/active_record/connection_adapters/sqlserver/quoting.rb
|
63
|
+
- lib/active_record/connection_adapters/sqlserver/schema_cache.rb
|
44
64
|
- lib/active_record/connection_adapters/sqlserver/schema_statements.rb
|
65
|
+
- lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb
|
66
|
+
- lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb
|
67
|
+
- lib/active_record/connection_adapters/sqlserver/showplan.rb
|
68
|
+
- lib/active_record/connection_adapters/sqlserver/utils.rb
|
45
69
|
- lib/active_record/connection_adapters/sqlserver/version.rb
|
46
70
|
- lib/active_record/connection_adapters/sqlserver_adapter.rb
|
47
71
|
- lib/activerecord-sqlserver-adapter.rb
|
48
72
|
- lib/arel/visitors/sqlserver.rb
|
49
73
|
homepage: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
50
74
|
licenses: []
|
75
|
+
|
51
76
|
post_install_message:
|
52
77
|
rdoc_options: []
|
53
|
-
|
78
|
+
|
79
|
+
require_paths:
|
54
80
|
- lib
|
55
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
82
|
none: false
|
57
|
-
requirements:
|
58
|
-
- -
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
|
61
|
-
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
91
|
none: false
|
63
|
-
requirements:
|
64
|
-
- -
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
|
92
|
+
requirements:
|
93
|
+
- - ">"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 25
|
96
|
+
segments:
|
97
|
+
- 1
|
98
|
+
- 3
|
99
|
+
- 1
|
100
|
+
version: 1.3.1
|
67
101
|
requirements: []
|
102
|
+
|
68
103
|
rubyforge_project: activerecord-sqlserver-adapter
|
69
|
-
rubygems_version: 1.8.
|
104
|
+
rubygems_version: 1.8.12
|
70
105
|
signing_key:
|
71
106
|
specification_version: 3
|
72
107
|
summary: SQL Server 2005 and 2008 Adapter For ActiveRecord.
|
73
108
|
test_files: []
|
109
|
+
|