activerecord-sqlserver-adapter 3.1.0.rc2 → 3.1.0.rc5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +13 -0
- data/README.rdoc +9 -25
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +3 -63
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +36 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +41 -16
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +16 -26
- data/lib/arel/visitors/sqlserver.rb +28 -14
- metadata +7 -7
data/CHANGELOG
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
|
2
2
|
* 3.1.0 *
|
3
3
|
|
4
|
+
* Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is
|
5
|
+
automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time
|
6
|
+
ActiveSupport formatter that is used per the language settings of the connection.
|
7
|
+
|
8
|
+
* Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update
|
9
|
+
that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833
|
10
|
+
|
11
|
+
* Allow drop_database to be called even when DB does not exist.
|
12
|
+
|
13
|
+
* Remove totally broken ADONET connection mode. Want it back, submit a patch.
|
14
|
+
|
15
|
+
* Schema reflection now finds primary key for all occasions. Fixed #60 [Boško Ivanišević]
|
16
|
+
|
4
17
|
* Allow complex order objects to not be molested by our visitor overrides. Fixes #99
|
5
18
|
|
6
19
|
* Default unicode datatypes!
|
data/README.rdoc
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
|
2
2
|
== SQL Server 2005/2008 & Azure Adapter For ActiveRecord
|
3
3
|
|
4
|
-
The SQL Server adapter for ActiveRecord.
|
4
|
+
The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails.
|
5
5
|
|
6
6
|
|
7
7
|
== What's New
|
8
8
|
|
9
9
|
* Rails 3.1 prepared statement support leverages cached query plans.
|
10
|
+
* We now support your native language date/time formats automatically!
|
10
11
|
* Default unicode datatypes! Disable with #enable_default_unicode_types to false.
|
11
12
|
* New #lowercase_schema_reflection configuration option for legacy DBs.
|
12
|
-
* New dblib connection mode using
|
13
|
+
* New dblib connection mode using TinyTDS! Default mode too!
|
13
14
|
|
14
15
|
|
15
16
|
==== Testing Rake Tasks Support
|
@@ -118,39 +119,22 @@ By default the adapter will auto connect to lost DB connections. For every query
|
|
118
119
|
|
119
120
|
== Versions
|
120
121
|
|
121
|
-
|
122
|
+
The adapter follows a rational versioning policy that also tracks ActiveRecord's major and minor version. That means the latest 3.1.x version of the adapter will always work for the latest 3.1.x version of ActiveRecord.
|
122
123
|
|
123
124
|
|
124
125
|
== Installation
|
125
126
|
|
126
|
-
|
127
|
+
The adapter has no strict gem dependencies outside of ActiveRecord. You will have to pick a connection mode, the default is dblib which uses the TinyTDS gem. Just bundle the gem and the adapter will use it.
|
127
128
|
|
128
|
-
|
129
|
+
gem 'tiny_tds'
|
130
|
+
gem 'activerecord-sqlserver-adapter', '~> 3.1.0'
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
gem 'activerecord-sqlserver-adapter', '3.x.xx'
|
133
|
-
|
134
|
-
If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too!
|
132
|
+
If you want to use ruby ODBC, please use at least version 0.99992 since that contains fixes for both native types as well as fixes for proper encoding support under 1.9. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too!
|
135
133
|
|
136
134
|
http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/platform-installation
|
137
135
|
|
138
136
|
|
139
137
|
|
140
|
-
== IronRuby ADONET Mode
|
141
|
-
|
142
|
-
A few details on this implementation. All that is needed in your database.yml configuration file is "mode: adonet" vs "odbc" and if you are running IronRuby, the connection will be native. You can also specify an "integrated_security: true" option in your configuration, remember to remove the username/password options too. To use this adapter, you will not need need ANY DBI middle layer or special extension gems to the adapter.
|
143
|
-
|
144
|
-
This adapter is opinionated in regards to IronRuby on types going in and out of the DB. For example strings will be String, not System::String and DateTime vs System::Datetime. There are many more examples but the rule of thumb is that the types will be simple types that correlate to a standard Ruby implementation. We enforce this basic rule because it is necessary to pass the tests and let the framework do its job. We recommend sticking to native Ruby types in your application code too.
|
145
|
-
|
146
|
-
The adapter establishes a System::Data::SqlClient connection that has both MultipleActiveResultSets (MARS) and Pooling turned off. There are good reasons for this one because the connection would not work otherwise for all the code issued by ActiveRecord. Remember too that ActiveRecord has it's own connection pooling and these underlying features like MARS/Pooling work against the adapter code.
|
147
|
-
|
148
|
-
Currently IronRuby is passing most of the ActiveRecord and Adapter tests. Here is a list of the ones remaining. Some are in the adapter's realm and some are in Marshaling area of IronRuby's core to fix. Feel like helping knock these out? Submit a patch to github issues.
|
149
|
-
|
150
|
-
http://gist.github.com/381101
|
151
|
-
|
152
|
-
|
153
|
-
|
154
138
|
== Contributing
|
155
139
|
|
156
140
|
If you’d like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Google list, IRC, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the RUNNING_UNIT_TESTS file for the details of how to run the unit tests.
|
@@ -182,7 +166,7 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-
|
|
182
166
|
* jeremydurham (Jeremy Durham)
|
183
167
|
|
184
168
|
=== Donators
|
185
|
-
http://pledgie.com/campaigns/
|
169
|
+
http://pledgie.com/campaigns/15531
|
186
170
|
|
187
171
|
== License
|
188
172
|
|
@@ -109,10 +109,6 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
results.many? ? results : results.first
|
112
|
-
when :adonet
|
113
|
-
results = []
|
114
|
-
results << select(sql, name).map { |r| r.with_indifferent_access }
|
115
|
-
results.many? ? results : results.first
|
116
112
|
end
|
117
113
|
end
|
118
114
|
end
|
@@ -183,6 +179,8 @@ module ActiveRecord
|
|
183
179
|
retry_count += 1
|
184
180
|
remove_database_connections_and_rollback(database)
|
185
181
|
retry
|
182
|
+
elsif err.message =~ /does not exist/i
|
183
|
+
nil
|
186
184
|
else
|
187
185
|
raise
|
188
186
|
end
|
@@ -241,7 +239,7 @@ module ActiveRecord
|
|
241
239
|
next if ar_column && column.sql_type == 'timestamp'
|
242
240
|
v = value
|
243
241
|
names_and_types << if ar_column
|
244
|
-
v = value.to_i if column.is_integer?
|
242
|
+
v = value.to_i if column.is_integer? && value.present?
|
245
243
|
"@#{index} #{column.sql_type_for_statement}"
|
246
244
|
elsif column.acts_like?(:string)
|
247
245
|
"@#{index} nvarchar(max)"
|
@@ -265,8 +263,6 @@ module ActiveRecord
|
|
265
263
|
@connection.execute(sql).do
|
266
264
|
when :odbc
|
267
265
|
@connection.do(sql)
|
268
|
-
else :adonet
|
269
|
-
@connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_non_query
|
270
266
|
end
|
271
267
|
ensure
|
272
268
|
@update_sql = false
|
@@ -292,8 +288,6 @@ module ActiveRecord
|
|
292
288
|
@connection.execute(sql)
|
293
289
|
when :odbc
|
294
290
|
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
|
295
|
-
else :adonet
|
296
|
-
@connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_reader
|
297
291
|
end
|
298
292
|
end
|
299
293
|
end
|
@@ -303,8 +297,6 @@ module ActiveRecord
|
|
303
297
|
when :dblib
|
304
298
|
when :odbc
|
305
299
|
handle.more_results
|
306
|
-
when :adonet
|
307
|
-
handle.next_result
|
308
300
|
end
|
309
301
|
end
|
310
302
|
|
@@ -314,8 +306,6 @@ module ActiveRecord
|
|
314
306
|
handle_to_names_and_values_dblib(handle, options)
|
315
307
|
when :odbc
|
316
308
|
handle_to_names_and_values_odbc(handle, options)
|
317
|
-
when :adonet
|
318
|
-
handle_to_names_and_values_adonet(handle, options)
|
319
309
|
end
|
320
310
|
end
|
321
311
|
|
@@ -344,62 +334,12 @@ module ActiveRecord
|
|
344
334
|
end
|
345
335
|
end
|
346
336
|
end
|
347
|
-
|
348
|
-
def handle_to_names_and_values_adonet(handle, options={})
|
349
|
-
if handle.has_rows
|
350
|
-
names = []
|
351
|
-
rows = []
|
352
|
-
fields_named = options[:fetch] == :rows
|
353
|
-
while handle.read
|
354
|
-
row = []
|
355
|
-
handle.visible_field_count.times do |row_index|
|
356
|
-
value = handle.get_value(row_index)
|
357
|
-
value = case value
|
358
|
-
when System::String
|
359
|
-
value.to_s
|
360
|
-
when System::DBNull
|
361
|
-
nil
|
362
|
-
when System::DateTime
|
363
|
-
value.to_string("yyyy-MM-dd HH:mm:ss.fff").to_s
|
364
|
-
when @@array_of_bytes ||= System::Array[System::Byte]
|
365
|
-
String.new(value)
|
366
|
-
else
|
367
|
-
value
|
368
|
-
end
|
369
|
-
row << value
|
370
|
-
names << handle.get_name(row_index).to_s unless fields_named
|
371
|
-
end
|
372
|
-
rows << row
|
373
|
-
fields_named = true
|
374
|
-
end
|
375
|
-
else
|
376
|
-
rows = []
|
377
|
-
end
|
378
|
-
if options[:fetch] != :rows
|
379
|
-
names_and_values = []
|
380
|
-
rows.each do |row|
|
381
|
-
h = {}
|
382
|
-
i = 0
|
383
|
-
while i < row.size
|
384
|
-
h[names[i]] = row[i]
|
385
|
-
i += 1
|
386
|
-
end
|
387
|
-
names_and_values << h
|
388
|
-
end
|
389
|
-
names_and_values
|
390
|
-
else
|
391
|
-
rows
|
392
|
-
end
|
393
|
-
end
|
394
337
|
|
395
338
|
def finish_statement_handle(handle)
|
396
339
|
case @connection_options[:mode]
|
397
340
|
when :dblib
|
398
341
|
when :odbc
|
399
342
|
handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
|
400
|
-
when :adonet
|
401
|
-
handle.close if handle && handle.respond_to?(:close) && !handle.is_closed
|
402
|
-
handle.dispose if handle && handle.respond_to?(:dispose)
|
403
343
|
end
|
404
344
|
handle
|
405
345
|
end
|
@@ -9,14 +9,12 @@ module ActiveRecord
|
|
9
9
|
|
10
10
|
LOST_CONNECTION_EXCEPTIONS = {
|
11
11
|
:dblib => ['TinyTds::Error'],
|
12
|
-
:odbc => ['ODBC::Error']
|
13
|
-
:adonet => ['TypeError','System::Data::SqlClient::SqlException']
|
12
|
+
:odbc => ['ODBC::Error']
|
14
13
|
}.freeze
|
15
14
|
|
16
15
|
LOST_CONNECTION_MESSAGES = {
|
17
16
|
:dblib => [/closed connection/, /dead or not enabled/, /server failed/i],
|
18
|
-
:odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
|
19
|
-
:adonet => [/current state is closed/, /network-related/]
|
17
|
+
:odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
|
20
18
|
}.freeze
|
21
19
|
|
22
20
|
|
@@ -8,13 +8,23 @@ module ActiveRecord
|
|
8
8
|
def quote(value, column = nil)
|
9
9
|
case value
|
10
10
|
when String, ActiveSupport::Multibyte::Chars
|
11
|
-
if column && column.type == :
|
11
|
+
if column && column.type == :integer && value.blank?
|
12
|
+
nil
|
13
|
+
elsif column && column.type == :binary
|
12
14
|
column.class.string_to_binary(value)
|
13
15
|
elsif value.is_utf8? || (column && column.type == :string)
|
14
16
|
"N'#{quote_string(value)}'"
|
15
17
|
else
|
16
18
|
super
|
17
19
|
end
|
20
|
+
when Date, Time
|
21
|
+
if column && column.sql_type == 'datetime'
|
22
|
+
"'#{quoted_datetime(value)}'"
|
23
|
+
elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time')
|
24
|
+
"'#{quoted_full_iso8601(value)}'"
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
18
28
|
when nil
|
19
29
|
column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
|
20
30
|
else
|
@@ -51,14 +61,39 @@ module ActiveRecord
|
|
51
61
|
QUOTED_FALSE
|
52
62
|
end
|
53
63
|
|
64
|
+
def quoted_datetime(value)
|
65
|
+
if value.acts_like?(:time)
|
66
|
+
value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(3).to(22)
|
67
|
+
else
|
68
|
+
quoted_date(value)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def quoted_full_iso8601(value)
|
73
|
+
if value.acts_like?(:time)
|
74
|
+
value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
|
75
|
+
else
|
76
|
+
quoted_date(value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
54
80
|
def quoted_date(value)
|
55
81
|
if value.acts_like?(:time) && value.respond_to?(:usec)
|
56
82
|
"#{super}.#{sprintf("%03d",value.usec/1000)}"
|
83
|
+
elsif value.acts_like?(:date)
|
84
|
+
value.to_s(:_sqlserver_dateformat)
|
57
85
|
else
|
58
86
|
super
|
59
87
|
end
|
60
88
|
end
|
61
89
|
|
90
|
+
protected
|
91
|
+
|
92
|
+
def quoted_value_acts_like_time_filter(value)
|
93
|
+
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
94
|
+
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
|
95
|
+
end
|
96
|
+
|
62
97
|
end
|
63
98
|
end
|
64
99
|
end
|
@@ -7,9 +7,9 @@ module ActiveRecord
|
|
7
7
|
@native_database_types ||= initialize_native_database_types.freeze
|
8
8
|
end
|
9
9
|
|
10
|
-
def tables(name = nil)
|
10
|
+
def tables(name = nil, table_type = 'BASE TABLE')
|
11
11
|
info_schema_query do
|
12
|
-
select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '
|
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
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -19,8 +19,8 @@ module ActiveRecord
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def indexes(table_name, name = nil)
|
22
|
-
|
23
|
-
|
22
|
+
data = select("EXEC sp_helpindex #{quote(table_name)}",name) rescue []
|
23
|
+
data.inject([]) do |indexes,index|
|
24
24
|
index = index.with_indifferent_access
|
25
25
|
if index[:index_description] =~ /primary key/
|
26
26
|
indexes
|
@@ -39,16 +39,31 @@ module ActiveRecord
|
|
39
39
|
|
40
40
|
def columns(table_name, name = nil)
|
41
41
|
return [] if table_name.blank?
|
42
|
-
column_definitions(table_name).collect do |ci|
|
42
|
+
@sqlserver_columns_cache[table_name] ||= column_definitions(table_name).collect do |ci|
|
43
43
|
sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
|
44
44
|
SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
def create_table(table_name, options = {})
|
49
|
+
super
|
50
|
+
clear_cache!
|
51
|
+
end
|
52
|
+
|
48
53
|
def rename_table(table_name, new_name)
|
49
54
|
do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
|
50
55
|
end
|
56
|
+
|
57
|
+
def drop_table(table_name, options = {})
|
58
|
+
super
|
59
|
+
clear_cache!
|
60
|
+
end
|
51
61
|
|
62
|
+
def add_column(table_name, column_name, type, options = {})
|
63
|
+
super
|
64
|
+
clear_cache!
|
65
|
+
end
|
66
|
+
|
52
67
|
def remove_column(table_name, *column_names)
|
53
68
|
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
54
69
|
column_names.flatten.each do |column_name|
|
@@ -57,6 +72,7 @@ module ActiveRecord
|
|
57
72
|
remove_indexes(table_name, column_name)
|
58
73
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
59
74
|
end
|
75
|
+
clear_cache!
|
60
76
|
end
|
61
77
|
|
62
78
|
def change_column(table_name, column_name, type, options = {})
|
@@ -72,16 +88,19 @@ module ActiveRecord
|
|
72
88
|
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)}"
|
73
89
|
end
|
74
90
|
sql_commands.each { |c| do_execute(c) }
|
91
|
+
clear_cache!
|
75
92
|
end
|
76
93
|
|
77
94
|
def change_column_default(table_name, column_name, default)
|
78
95
|
remove_default_constraint(table_name, column_name)
|
79
96
|
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)}"
|
97
|
+
clear_cache!
|
80
98
|
end
|
81
99
|
|
82
100
|
def rename_column(table_name, column_name, new_column_name)
|
83
101
|
detect_column_for!(table_name,column_name)
|
84
102
|
do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
|
103
|
+
clear_cache!
|
85
104
|
end
|
86
105
|
|
87
106
|
def remove_index!(table_name, index_name)
|
@@ -117,8 +136,7 @@ module ActiveRecord
|
|
117
136
|
# === SQLServer Specific ======================================== #
|
118
137
|
|
119
138
|
def views(name = nil)
|
120
|
-
@sqlserver_views_cache ||=
|
121
|
-
info_schema_query { select_values("SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME NOT IN ('sysconstraints','syssegments')") }
|
139
|
+
@sqlserver_views_cache ||= tables(name,'VIEW')
|
122
140
|
end
|
123
141
|
|
124
142
|
|
@@ -150,36 +168,41 @@ module ActiveRecord
|
|
150
168
|
:ss_timestamp => { :name => 'timestamp' }
|
151
169
|
}
|
152
170
|
end
|
153
|
-
|
171
|
+
|
154
172
|
def column_definitions(table_name)
|
155
173
|
db_name = unqualify_db_name(table_name)
|
156
174
|
db_name_with_period = "#{db_name}." if db_name
|
157
175
|
table_schema = unqualify_table_schema(table_name)
|
158
176
|
table_name = unqualify_table_name(table_name)
|
159
177
|
sql = %{
|
160
|
-
SELECT
|
178
|
+
SELECT DISTINCT
|
161
179
|
#{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
|
162
180
|
#{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name,
|
163
181
|
columns.DATA_TYPE AS type,
|
164
182
|
columns.COLUMN_DEFAULT AS default_value,
|
165
183
|
columns.NUMERIC_SCALE AS numeric_scale,
|
166
184
|
columns.NUMERIC_PRECISION AS numeric_precision,
|
185
|
+
columns.ordinal_position,
|
167
186
|
CASE
|
168
187
|
WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
|
169
188
|
ELSE COL_LENGTH(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME)
|
170
|
-
END AS length,
|
189
|
+
END AS [length],
|
171
190
|
CASE
|
172
191
|
WHEN columns.IS_NULLABLE = 'YES' THEN 1
|
173
192
|
ELSE NULL
|
174
|
-
END AS is_nullable,
|
175
|
-
CASE
|
176
|
-
WHEN
|
177
|
-
|
178
|
-
|
193
|
+
END AS [is_nullable],
|
194
|
+
CASE
|
195
|
+
WHEN CCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1
|
196
|
+
WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 1 THEN 1
|
197
|
+
ELSE NULL
|
198
|
+
END AS [is_identity]
|
179
199
|
FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns
|
200
|
+
LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
|
201
|
+
LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS CCU ON TC.CONSTRAINT_NAME = CCU.CONSTRAINT_NAME AND CCU.COLUMN_NAME = columns.COLUMN_NAME
|
180
202
|
WHERE columns.TABLE_NAME = @0
|
181
203
|
AND columns.TABLE_SCHEMA = #{table_schema.blank? ? "schema_name()" : "@1"}
|
182
204
|
ORDER BY columns.ordinal_position
|
205
|
+
|
183
206
|
}.gsub(/[ \t\r\n]+/,' ')
|
184
207
|
binds = [['table_name', table_name]]
|
185
208
|
binds << ['table_schema',table_schema] unless table_schema.blank?
|
@@ -213,6 +236,7 @@ module ActiveRecord
|
|
213
236
|
match_data ? match_data[1] : nil
|
214
237
|
end
|
215
238
|
ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
|
239
|
+
ci[:is_identity] = ci[:is_identity].to_i == 1
|
216
240
|
ci
|
217
241
|
end
|
218
242
|
end
|
@@ -324,6 +348,7 @@ module ActiveRecord
|
|
324
348
|
# === SQLServer Specific (Column/View Caches) =================== #
|
325
349
|
|
326
350
|
def initialize_sqlserver_caches
|
351
|
+
@sqlserver_columns_cache = {}
|
327
352
|
@sqlserver_views_cache = nil
|
328
353
|
@sqlserver_view_information_cache = {}
|
329
354
|
@sqlserver_quoted_column_and_table_names = {}
|
@@ -361,7 +386,7 @@ module ActiveRecord
|
|
361
386
|
end
|
362
387
|
|
363
388
|
def identity_column(table_name)
|
364
|
-
columns(table_name).detect(&:is_identity?)
|
389
|
+
columns(table_name).detect(&:primary) || columns(table_name).detect(&:is_identity?)
|
365
390
|
end
|
366
391
|
|
367
392
|
end
|
@@ -21,15 +21,12 @@ module ActiveRecord
|
|
21
21
|
mode = config[:mode].to_s.downcase.underscore.to_sym
|
22
22
|
case mode
|
23
23
|
when :dblib
|
24
|
-
|
24
|
+
require 'tiny_tds'
|
25
25
|
warn("TinyTds v0.4.3 or higher required. Using #{TinyTds::VERSION}") unless TinyTds::Client.instance_methods.map(&:to_s).include?("active?")
|
26
26
|
when :odbc
|
27
27
|
raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
|
28
|
-
|
28
|
+
require 'odbc'
|
29
29
|
require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
|
30
|
-
when :adonet
|
31
|
-
require 'System.Data'
|
32
|
-
raise ArgumentError, 'Missing :database configuration.' unless config.has_key?(:database)
|
33
30
|
else
|
34
31
|
raise ArgumentError, "Unknown connection mode in #{config.inspect}."
|
35
32
|
end
|
@@ -55,6 +52,7 @@ module ActiveRecord
|
|
55
52
|
def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
|
56
53
|
@sqlserver_options = sqlserver_options.symbolize_keys
|
57
54
|
super(name, default, sql_type, null)
|
55
|
+
@primary = @sqlserver_options[:is_identity]
|
58
56
|
end
|
59
57
|
|
60
58
|
class << self
|
@@ -74,11 +72,11 @@ module ActiveRecord
|
|
74
72
|
end
|
75
73
|
|
76
74
|
def is_utf8?
|
77
|
-
@sql_type =~ /nvarchar|ntext|nchar/i
|
75
|
+
!!(@sql_type =~ /nvarchar|ntext|nchar/i)
|
78
76
|
end
|
79
77
|
|
80
78
|
def is_integer?
|
81
|
-
@sql_type =~ /int/i
|
79
|
+
!!(@sql_type =~ /int/i)
|
82
80
|
end
|
83
81
|
|
84
82
|
def sql_type_for_statement
|
@@ -161,7 +159,7 @@ module ActiveRecord
|
|
161
159
|
include Sqlserver::Errors
|
162
160
|
|
163
161
|
ADAPTER_NAME = 'SQLServer'.freeze
|
164
|
-
VERSION = '3.1.0.
|
162
|
+
VERSION = '3.1.0.rc5'.freeze
|
165
163
|
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
|
166
164
|
SUPPORTED_VERSIONS = [2005,2008,2010,2011].freeze
|
167
165
|
|
@@ -190,6 +188,7 @@ module ActiveRecord
|
|
190
188
|
rescue
|
191
189
|
0
|
192
190
|
end
|
191
|
+
initialize_dateformatter
|
193
192
|
initialize_sqlserver_caches
|
194
193
|
use_database
|
195
194
|
unless SUPPORTED_VERSIONS.include?(@database_year)
|
@@ -255,8 +254,6 @@ module ActiveRecord
|
|
255
254
|
@connection.close rescue nil
|
256
255
|
when :odbc
|
257
256
|
@connection.disconnect rescue nil
|
258
|
-
else :adonet
|
259
|
-
@connection.close rescue nil
|
260
257
|
end
|
261
258
|
end
|
262
259
|
|
@@ -409,27 +406,20 @@ module ActiveRecord
|
|
409
406
|
warn "Ruby ODBC v0.99992 or higher is required."
|
410
407
|
end
|
411
408
|
end
|
412
|
-
when :adonet
|
413
|
-
System::Data::SqlClient::SqlConnection.new.tap do |connection|
|
414
|
-
connection.connection_string = System::Data::SqlClient::SqlConnectionStringBuilder.new.tap do |cs|
|
415
|
-
if config[:integrated_security]
|
416
|
-
cs.integrated_security = true
|
417
|
-
else
|
418
|
-
cs.user_i_d = config[:username]
|
419
|
-
cs.password = config[:password]
|
420
|
-
end
|
421
|
-
cs.add 'Server', config[:host].to_clr_string
|
422
|
-
cs.initial_catalog = config[:database]
|
423
|
-
cs.multiple_active_result_sets = false
|
424
|
-
cs.pooling = false
|
425
|
-
end.to_s
|
426
|
-
connection.open
|
427
|
-
end
|
428
409
|
end
|
429
410
|
rescue
|
430
411
|
raise unless @auto_connecting
|
431
412
|
end
|
432
413
|
|
414
|
+
def initialize_dateformatter
|
415
|
+
@database_dateformat = user_options['dateformat']
|
416
|
+
a, b, c = @database_dateformat.each_char.to_a
|
417
|
+
[a,b,c].each { |f| f.upcase! if f == 'y' }
|
418
|
+
dateformat = "%#{a}-%#{b}-%#{c}"
|
419
|
+
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
420
|
+
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
421
|
+
end
|
422
|
+
|
433
423
|
def remove_database_connections_and_rollback(database=nil)
|
434
424
|
database ||= current_database
|
435
425
|
do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
|
@@ -6,12 +6,12 @@ module Arel
|
|
6
6
|
|
7
7
|
# Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
|
8
8
|
# collection of them. See SelectManager#order for more details.
|
9
|
-
class Ordering < Arel::Nodes::
|
9
|
+
class Ordering < Arel::Nodes::Unary
|
10
10
|
def hash
|
11
11
|
expr.hash
|
12
12
|
end
|
13
13
|
def ==(other)
|
14
|
-
|
14
|
+
other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr
|
15
15
|
end
|
16
16
|
def eql?(other)
|
17
17
|
self == other
|
@@ -27,26 +27,25 @@ module Arel
|
|
27
27
|
# to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
|
28
28
|
# is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects.
|
29
29
|
alias :order_without_sqlserver :order
|
30
|
-
def order(*
|
31
|
-
return order_without_sqlserver(*
|
32
|
-
@ast.orders.concat(
|
30
|
+
def order(*expr)
|
31
|
+
return order_without_sqlserver(*expr) unless Arel::Visitors::SQLServer === @visitor
|
32
|
+
@ast.orders.concat(expr.map{ |x|
|
33
33
|
case x
|
34
34
|
when Arel::Attributes::Attribute
|
35
35
|
table = Arel::Table.new(x.relation.table_alias || x.relation.name)
|
36
|
-
|
37
|
-
Arel::Nodes::
|
36
|
+
e = table[x.name]
|
37
|
+
Arel::Nodes::Ascending.new e
|
38
38
|
when Arel::Nodes::Ordering
|
39
39
|
x
|
40
40
|
when String
|
41
41
|
x.split(',').map do |s|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Arel::Nodes::Ordering.new expr, direction
|
42
|
+
e, d = s.split
|
43
|
+
e = Arel.sql(e)
|
44
|
+
d =~ /desc/i ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e)
|
46
45
|
end
|
47
46
|
else
|
48
|
-
|
49
|
-
Arel::Nodes::
|
47
|
+
e = Arel.sql(x.to_s)
|
48
|
+
Arel::Nodes::Ascending.new e
|
50
49
|
end
|
51
50
|
}.flatten)
|
52
51
|
self
|
@@ -89,6 +88,13 @@ module Arel
|
|
89
88
|
visit_Arel_Nodes_SelectStatementWithOutOffset(o)
|
90
89
|
end
|
91
90
|
end
|
91
|
+
|
92
|
+
def visit_Arel_Nodes_UpdateStatement(o)
|
93
|
+
if o.orders.any? && o.limit.nil?
|
94
|
+
o.limit = Nodes::Limit.new(2147483647)
|
95
|
+
end
|
96
|
+
super
|
97
|
+
end
|
92
98
|
|
93
99
|
def visit_Arel_Nodes_Offset(o)
|
94
100
|
"WHERE [__rnt].[__rn] > (#{visit o.expr})"
|
@@ -101,7 +107,15 @@ module Arel
|
|
101
107
|
def visit_Arel_Nodes_Lock(o)
|
102
108
|
visit o.expr
|
103
109
|
end
|
104
|
-
|
110
|
+
|
111
|
+
def visit_Arel_Nodes_Ordering(o)
|
112
|
+
if o.respond_to?(:direction)
|
113
|
+
"#{visit o.expr} #{o.ascending? ? 'ASC' : 'DESC'}"
|
114
|
+
else
|
115
|
+
visit o.expr
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
105
119
|
def visit_Arel_Nodes_Bin(o)
|
106
120
|
"#{visit o.expr} #{@engine.connection.cs_equality_operator}"
|
107
121
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-sqlserver-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15424111
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 1
|
9
9
|
- 0
|
10
10
|
- rc
|
11
|
-
-
|
12
|
-
version: 3.1.0.
|
11
|
+
- 5
|
12
|
+
version: 3.1.0.rc5
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Ken Collins
|
@@ -21,7 +21,7 @@ autorequire:
|
|
21
21
|
bindir: bin
|
22
22
|
cert_chain: []
|
23
23
|
|
24
|
-
date: 2011-
|
24
|
+
date: 2011-07-26 00:00:00 -04:00
|
25
25
|
default_executable:
|
26
26
|
dependencies:
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -32,14 +32,14 @@ dependencies:
|
|
32
32
|
requirements:
|
33
33
|
- - ~>
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
hash:
|
35
|
+
hash: 15424111
|
36
36
|
segments:
|
37
37
|
- 3
|
38
38
|
- 1
|
39
39
|
- 0
|
40
40
|
- rc
|
41
|
-
-
|
42
|
-
version: 3.1.0.
|
41
|
+
- 5
|
42
|
+
version: 3.1.0.rc5
|
43
43
|
type: :runtime
|
44
44
|
version_requirements: *id001
|
45
45
|
description: SQL Server 2005 and 2008 Adapter For ActiveRecord
|