activerecord-sqlserver-adapter 3.1.7 → 3.2.0.rc1
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 +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
|
+
|