mssqlclient 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/RAKEFILE +68 -0
- data/README +1 -0
- data/lib/column_with_identity_and_ordinal.rb +92 -0
- data/lib/ms_sql_client.dll +0 -0
- data/lib/mssqlclient.rb +1 -0
- data/lib/mssqlclient_adapter.rb +391 -0
- data/src/AssemblyInfo.cpp +38 -0
- data/src/Helpers.cpp +177 -0
- data/src/Helpers.h +44 -0
- data/src/MsSqlClient..express.sln +20 -0
- data/src/MsSqlClient.cpp +196 -0
- data/src/MsSqlClient.express.vcproj +242 -0
- data/src/MsSqlClient.h +13 -0
- data/src/MsSqlClient.sln +20 -0
- data/src/MsSqlClient.vcproj +242 -0
- data/src/app.ico +0 -0
- data/src/app.rc +63 -0
- data/src/resource.h +3 -0
- metadata +83 -0
data/CHANGELOG
ADDED
data/RAKEFILE
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/contrib/rubyforgepublisher'
|
8
|
+
require 'pscp'
|
9
|
+
|
10
|
+
PACKAGE_VERSION = '0.1.0'
|
11
|
+
|
12
|
+
PACKAGE_FILES = FileList[
|
13
|
+
'README',
|
14
|
+
'CHANGELOG',
|
15
|
+
'RAKEFILE',
|
16
|
+
'lib/**/*.{rb,dll}',
|
17
|
+
'test/*.rb',
|
18
|
+
'src/*.{cpp,h,ico,rc,sln,vcproj}'
|
19
|
+
].to_a
|
20
|
+
|
21
|
+
PROJECT = 'mssqlclient'
|
22
|
+
|
23
|
+
ENV['RUBYFORGE_USER'] = "ssmoot@rubyforge.org"
|
24
|
+
ENV['RUBYFORGE_PROJECT'] = "/var/www/gforge-projects/#{PROJECT}"
|
25
|
+
|
26
|
+
task :default => [:rdoc]
|
27
|
+
|
28
|
+
desc 'Generate Documentation'
|
29
|
+
rd = Rake::RDocTask.new do |rdoc|
|
30
|
+
rdoc.rdoc_dir = 'doc'
|
31
|
+
rdoc.title = "MsSqlClient -- A native Win32/ADO.NET ActiveRecord Adapter for Microsoft's SQL Server"
|
32
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
33
|
+
rdoc.rdoc_files.include(PACKAGE_FILES)
|
34
|
+
end
|
35
|
+
|
36
|
+
gem_spec = Gem::Specification.new do |s|
|
37
|
+
s.platform = Gem::Platform::RUBY
|
38
|
+
s.name = PROJECT
|
39
|
+
s.summary = "A native Win32/ADO.NET ActiveRecord Adapter for Microsoft's SQL Server"
|
40
|
+
s.description = "A faster, better way to integrate ActiveRecord with MS SQL Server"
|
41
|
+
s.version = PACKAGE_VERSION
|
42
|
+
|
43
|
+
s.authors = 'Sam Smoot', 'Scott Bauer'
|
44
|
+
s.email = 'ssmoot@gmail.com; bauer.mail@gmail.com'
|
45
|
+
s.rubyforge_project = PROJECT
|
46
|
+
s.homepage = 'http://substantiality.net'
|
47
|
+
|
48
|
+
s.files = PACKAGE_FILES
|
49
|
+
|
50
|
+
s.require_path = 'lib'
|
51
|
+
s.requirements << 'active_record'
|
52
|
+
s.autorequire = 'mssqlclient_adapter'
|
53
|
+
|
54
|
+
s.has_rdoc = true
|
55
|
+
s.rdoc_options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
56
|
+
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
Rake::GemPackageTask.new(gem_spec) do |p|
|
60
|
+
p.gem_spec = gem_spec
|
61
|
+
p.need_tar = true
|
62
|
+
p.need_zip = true
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Publish RDOC to RubyForge"
|
66
|
+
task :rubyforge => [:rdoc, :gem] do
|
67
|
+
Rake::SshDirPublisher.new(ENV['RUBYFORGE_USER'], ENV['RUBYFORGE_PROJECT'], 'doc').upload
|
68
|
+
end
|
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
This is the README! Interesting!
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class ColumnWithIdentityAndOrdinal < Column# :nodoc:
|
4
|
+
attr_reader :identity, :is_special, :scale
|
5
|
+
|
6
|
+
def initialize(name, default, sql_type = nil, is_identity = false, null = true, scale_value = 0, ordinal = 0)
|
7
|
+
super(name, default, sql_type, null)
|
8
|
+
@identity = is_identity
|
9
|
+
@is_special = sql_type =~ /text|ntext|image/i ? true : false
|
10
|
+
@scale = scale_value
|
11
|
+
# SQL Server only supports limits on *char and float types
|
12
|
+
@limit = nil unless @type == :float or @type == :string
|
13
|
+
@ordinal = ordinal
|
14
|
+
end
|
15
|
+
|
16
|
+
def simplified_type(field_type)
|
17
|
+
case field_type
|
18
|
+
when /int|bigint|smallint|tinyint/i then :integer
|
19
|
+
when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float
|
20
|
+
when /datetime|smalldatetime/i then :datetime
|
21
|
+
when /timestamp/i then :timestamp
|
22
|
+
when /time/i then :time
|
23
|
+
when /text|ntext/i then :text
|
24
|
+
when /binary|image|varbinary/i then :binary
|
25
|
+
when /char|nchar|nvarchar|string|varchar/i then :string
|
26
|
+
when /bit/i then :boolean
|
27
|
+
when /uniqueidentifier/i then :string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def type_cast(value)
|
32
|
+
return nil if value.nil? || value =~ /^\s*null\s*$/i
|
33
|
+
case type
|
34
|
+
when :string then value
|
35
|
+
when :integer then value == true || value == false ? value == true ? 1 : 0 : value.to_i
|
36
|
+
when :float then value.to_f
|
37
|
+
when :datetime then cast_to_datetime(value)
|
38
|
+
when :timestamp then cast_to_time(value)
|
39
|
+
when :time then cast_to_time(value)
|
40
|
+
when :date then cast_to_datetime(value)
|
41
|
+
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
|
42
|
+
else value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def cast_to_time(value)
|
47
|
+
return value if value.is_a?(Time)
|
48
|
+
time_array = ParseDate.parsedate(value)
|
49
|
+
time_array[0] ||= 2000
|
50
|
+
time_array[1] ||= 1
|
51
|
+
time_array[2] ||= 1
|
52
|
+
Time.send(Base.default_timezone, *time_array) rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def cast_to_datetime(value)
|
56
|
+
if value.is_a?(Time)
|
57
|
+
if value.year != 0 and value.month != 0 and value.day != 0
|
58
|
+
return value
|
59
|
+
else
|
60
|
+
return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
67
|
+
# These methods will only allow the adapter to insert binary data with a length of 7K or less
|
68
|
+
# because of a SQL Server statement length policy.
|
69
|
+
def self.string_to_binary(value)
|
70
|
+
value.gsub(/(\r|\n|\0|\x1a)/) do
|
71
|
+
case $1
|
72
|
+
when "\r" then "%00"
|
73
|
+
when "\n" then "%01"
|
74
|
+
when "\0" then "%02"
|
75
|
+
when "\x1a" then "%03"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.binary_to_string(value)
|
81
|
+
value.gsub(/(%00|%01|%02|%03)/) do
|
82
|
+
case $1
|
83
|
+
when "%00" then "\r"
|
84
|
+
when "%01" then "\n"
|
85
|
+
when "%02\0" then "\0"
|
86
|
+
when "%03" then "\x1a"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
Binary file
|
data/lib/mssqlclient.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'mssqlclient_adapter'
|
@@ -0,0 +1,391 @@
|
|
1
|
+
require 'active_record' # 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
RAILS_CONNECTION_ADAPTERS << 'mssqlclient'
|
3
|
+
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'ms_sql_client'
|
6
|
+
require 'column_with_identity_and_ordinal'
|
7
|
+
|
8
|
+
module ActiveRecord
|
9
|
+
class Base
|
10
|
+
def self.mssqlclient_connection(config) #:nodoc:
|
11
|
+
ConnectionAdapters::MsSqlClientAdapter.new(logger, config.symbolize_keys)
|
12
|
+
end
|
13
|
+
end # class Base
|
14
|
+
|
15
|
+
module ConnectionAdapters
|
16
|
+
class MsSqlClientAdapter < AbstractAdapter
|
17
|
+
|
18
|
+
include MsSqlClient
|
19
|
+
|
20
|
+
def initialize(logger, connection_options = nil)
|
21
|
+
@connection_options, @logger = connection_options, logger
|
22
|
+
@connection = self
|
23
|
+
@runtime = @last_verification = 0
|
24
|
+
@active = true
|
25
|
+
@connection_options[:schema] ||= 'dbo'
|
26
|
+
end
|
27
|
+
|
28
|
+
def connection_string
|
29
|
+
if @connection_options[:trusted]
|
30
|
+
"Server=#{@connection_options[:host]};Database=#{@connection_options[:database]};Trusted_Connection=True;"
|
31
|
+
elsif @connection_options[:connection_string]
|
32
|
+
@connection_options[:connection_string]
|
33
|
+
else
|
34
|
+
"Data Source=#{@connection_options[:host]};Initial Catalog=#{@connection_options[:database]};User Id=#{@connection_options[:username]};Password=#{@connection_options[:password]};";
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def native_database_types
|
39
|
+
{
|
40
|
+
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
|
41
|
+
:string => { :name => "varchar", :limit => 255 },
|
42
|
+
:text => { :name => "text" },
|
43
|
+
:integer => { :name => "int" },
|
44
|
+
:float => { :name => "float", :limit => 8 },
|
45
|
+
:datetime => { :name => "datetime" },
|
46
|
+
:timestamp => { :name => "datetime" },
|
47
|
+
:time => { :name => "datetime" },
|
48
|
+
:date => { :name => "datetime" },
|
49
|
+
:binary => { :name => "image"},
|
50
|
+
:boolean => { :name => "bit"}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def adapter_name
|
55
|
+
'MsSqlClient'
|
56
|
+
end
|
57
|
+
|
58
|
+
def supports_migrations? #:nodoc:
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
# CONNECTION MANAGEMENT ====================================#
|
63
|
+
|
64
|
+
def verify!(timeout)
|
65
|
+
@last_verification = Time.now.to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns true if the connection is active.
|
69
|
+
def active?
|
70
|
+
@active
|
71
|
+
end
|
72
|
+
|
73
|
+
# Reconnects to the database, returns false if no connection could be made.
|
74
|
+
def reconnect!
|
75
|
+
@active = true
|
76
|
+
end
|
77
|
+
|
78
|
+
# Disconnects from the database
|
79
|
+
|
80
|
+
def disconnect!
|
81
|
+
@active = false
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def select_all(sql, name=nil)
|
86
|
+
select(sql)
|
87
|
+
end
|
88
|
+
|
89
|
+
#alias_method :select_all, :select
|
90
|
+
|
91
|
+
def select_one(sql, name = nil)
|
92
|
+
add_limit!(sql, :limit => 1)
|
93
|
+
result = select(sql, name)
|
94
|
+
result.nil? ? nil : result.first
|
95
|
+
end
|
96
|
+
|
97
|
+
def columns(table_name, name = nil)
|
98
|
+
return [] if table_name.blank?
|
99
|
+
table_name = table_name.to_s if table_name.is_a?(Symbol)
|
100
|
+
table_name = table_name.split('.')[-1] unless table_name.nil?
|
101
|
+
sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, IS_NULLABLE as IsNullable, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '#{table_name}' AND TABLE_SCHEMA = '#{@connection_options[:schema]}'"
|
102
|
+
# puts sql
|
103
|
+
# Comment out if you want to have the Columns select statment logged.
|
104
|
+
# Personnally, I think it adds unneccessary bloat to the log.
|
105
|
+
# If you do comment it out, make sure to un-comment the "result" line that follows
|
106
|
+
result = log(sql, name) { _select(sql) }
|
107
|
+
#result = @connection.select_all(sql)
|
108
|
+
columns = []
|
109
|
+
result.each { |field| field.symbolize_keys!; columns << ColumnWithIdentityAndOrdinal.new(field[:ColName], field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue], "#{field[:ColType]}(#{field[:Length]})", field[:IsIdentity] == 1 ? true : false, field[:IsNullable] == 'YES', field[:Scale], field[:Ordinal]) }
|
110
|
+
# puts columns.inspect
|
111
|
+
columns
|
112
|
+
end
|
113
|
+
|
114
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
115
|
+
table_name = get_table_name(sql)
|
116
|
+
col = get_identity_column(table_name)
|
117
|
+
ii_enabled = false
|
118
|
+
|
119
|
+
if col != nil
|
120
|
+
if query_contains_identity_column(sql, col)
|
121
|
+
sql = wrap_identity_insert(table_name, sql)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
log(sql, name) do
|
126
|
+
new_id = _insert(sql)
|
127
|
+
id_value || new_id
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def execute(sql, name = nil)
|
132
|
+
if sql =~ /^\s*INSERT/i
|
133
|
+
insert(sql, name)
|
134
|
+
else
|
135
|
+
# puts "About to execute"
|
136
|
+
# sleep 1
|
137
|
+
log(sql, name) { _execute(sql) }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def update(sql, name = nil)
|
142
|
+
execute(sql, name)
|
143
|
+
end
|
144
|
+
alias_method :delete, :update
|
145
|
+
|
146
|
+
def quote(value, column = nil)
|
147
|
+
case value
|
148
|
+
when String
|
149
|
+
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
150
|
+
"'#{quote_string(column.class.string_to_binary(value))}'"
|
151
|
+
else
|
152
|
+
"N'#{quote_string(value)}'"
|
153
|
+
end
|
154
|
+
when NilClass then "NULL"
|
155
|
+
when TrueClass then '1'
|
156
|
+
when FalseClass then '0'
|
157
|
+
when Float, Fixnum, Bignum then value.to_s
|
158
|
+
when Date then "'#{value.to_s}'"
|
159
|
+
when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
160
|
+
else "'#{quote_string(value.to_yaml)}'"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def quote_string(string)
|
165
|
+
string.gsub(/\'/, "''")
|
166
|
+
end
|
167
|
+
|
168
|
+
def quoted_true
|
169
|
+
"1"
|
170
|
+
end
|
171
|
+
|
172
|
+
def quoted_false
|
173
|
+
"0"
|
174
|
+
end
|
175
|
+
|
176
|
+
def quote_column_name(name)
|
177
|
+
"[#{name}]"
|
178
|
+
end
|
179
|
+
|
180
|
+
def add_limit_offset!(sql, options)
|
181
|
+
# STDOUT << "\n\nadd_limit_offset options = #{options.inspect}\n\n"
|
182
|
+
# STDOUT << "add_limit_offset sql = #{sql}\n\n"
|
183
|
+
|
184
|
+
if options[:limit] and options[:offset]
|
185
|
+
sub_query = sql.gsub /\bSELECT\b(\s*DISTINCT)?/i do
|
186
|
+
"SELECT #{$1} TOP 1000000000"
|
187
|
+
end
|
188
|
+
|
189
|
+
total_rows = select("SELECT count(*) as TotalRows from (#{sub_query}) tally")[0]['TotalRows'].to_i
|
190
|
+
|
191
|
+
if (options[:limit] + options[:offset]) >= total_rows
|
192
|
+
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
|
193
|
+
end
|
194
|
+
sql.sub!(/^\s*SELECT/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT TOP #{options[:limit] + options[:offset]} ")
|
195
|
+
sql << ") AS tmp1"
|
196
|
+
|
197
|
+
# (SELECT TOP 0 * FROM (SELECT TOP 10 DISTINCT posts.id
|
198
|
+
|
199
|
+
sql.sub! /\(SELECT\sTOP\s(\d+)\s+DISTINCT/ do
|
200
|
+
"(SELECT DISTINCT TOP #{$1} "
|
201
|
+
end
|
202
|
+
|
203
|
+
if options[:order]
|
204
|
+
options[:order] = options[:order].split(',').map do |field|
|
205
|
+
parts = field.split(" ")
|
206
|
+
tc = parts[0]
|
207
|
+
if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
|
208
|
+
tc.gsub!(/\./, '\\.\\[')
|
209
|
+
tc << '\\]'
|
210
|
+
end
|
211
|
+
if sql =~ /#{tc} AS (t\d_r\d\d?)/
|
212
|
+
parts[0] = $1
|
213
|
+
end
|
214
|
+
parts.join(' ')
|
215
|
+
end.join(', ')
|
216
|
+
sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
|
217
|
+
else
|
218
|
+
sql << " ) AS tmp2"
|
219
|
+
end
|
220
|
+
elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
|
221
|
+
sql.sub!(/^\s*SELECT([\s]*distinct)?/i) do
|
222
|
+
"SELECT#{$1} TOP #{options[:limit]}"
|
223
|
+
end unless options[:limit].nil?
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def recreate_database(name)
|
228
|
+
drop_database(name)
|
229
|
+
create_database(name)
|
230
|
+
end
|
231
|
+
|
232
|
+
def drop_database(name)
|
233
|
+
execute "DROP DATABASE #{name}"
|
234
|
+
end
|
235
|
+
|
236
|
+
def create_database(name)
|
237
|
+
execute "CREATE DATABASE #{name}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def current_database
|
241
|
+
@connection.select_one("select DB_NAME() as [Name]")['Name']
|
242
|
+
end
|
243
|
+
|
244
|
+
def tables(name = nil)
|
245
|
+
results = select("SELECT table_name from information_schema.tables WHERE table_type = 'BASE TABLE'", name)
|
246
|
+
|
247
|
+
results.inject([]) do |tables, field|
|
248
|
+
table_name = field['table_name']
|
249
|
+
tables << table_name unless table_name == 'dtproperties'
|
250
|
+
tables
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def indexes(table_name, name = nil)
|
255
|
+
indexes = []
|
256
|
+
select("EXEC sp_helpindex #{table_name}", name).each do |index|
|
257
|
+
unique = index['index_description'] =~ /unique/
|
258
|
+
primary = index['index_description'] =~ /primary key/
|
259
|
+
if !primary
|
260
|
+
indexes << IndexDefinition.new(table_name, index['index_name'], unique, index['index_keys'].split(", "))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
indexes
|
264
|
+
end
|
265
|
+
|
266
|
+
def rename_table(name, new_name)
|
267
|
+
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
268
|
+
end
|
269
|
+
|
270
|
+
def remove_column(table_name, column_name)
|
271
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
|
272
|
+
end
|
273
|
+
|
274
|
+
def rename_column(table, column, new_column_name)
|
275
|
+
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
276
|
+
end
|
277
|
+
|
278
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
279
|
+
sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"]
|
280
|
+
if options[:default]
|
281
|
+
remove_default_constraint(table_name, column_name)
|
282
|
+
sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{options[:default]} FOR #{column_name}"
|
283
|
+
end
|
284
|
+
sql_commands.each {|c|
|
285
|
+
execute(c)
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
289
|
+
def remove_column(table_name, column_name)
|
290
|
+
remove_default_constraint(table_name, column_name)
|
291
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
|
292
|
+
end
|
293
|
+
|
294
|
+
def remove_default_constraint(table_name, column_name)
|
295
|
+
defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
|
296
|
+
defaults.each {|constraint|
|
297
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
301
|
+
def remove_index(table_name, options = {})
|
302
|
+
execute "DROP INDEX #{table_name}.[#{index_name(table_name, options)}]"
|
303
|
+
end
|
304
|
+
|
305
|
+
def type_to_sql(type, limit = nil) #:nodoc:
|
306
|
+
native = native_database_types[type]
|
307
|
+
# if there's no :limit in the default type definition, assume that type doesn't support limits
|
308
|
+
limit = limit || native[:limit]
|
309
|
+
column_type_sql = native[:name]
|
310
|
+
column_type_sql << "(#{limit})" if limit
|
311
|
+
column_type_sql
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
def select(sql, name = nil)
|
316
|
+
repair_special_columns(sql)
|
317
|
+
log(sql, name) do
|
318
|
+
_select(sql)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def wrap_identity_insert(table_name, sql)
|
323
|
+
if has_identity_column(table_name)
|
324
|
+
<<-SQL
|
325
|
+
SET IDENTITY_INSERT #{table_name} ON
|
326
|
+
#{sql}
|
327
|
+
SET IDENTITY_INSERT #{table_name} OFF
|
328
|
+
SQL
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def get_table_name(sql)
|
333
|
+
if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
334
|
+
$1
|
335
|
+
elsif sql =~ /from\s+([^\(\s]+)\s*/i
|
336
|
+
$1
|
337
|
+
else
|
338
|
+
nil
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def has_identity_column(table_name)
|
343
|
+
!get_identity_column(table_name).nil?
|
344
|
+
end
|
345
|
+
|
346
|
+
def get_identity_column(table_name)
|
347
|
+
@table_columns = {} unless @table_columns
|
348
|
+
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
|
349
|
+
@table_columns[table_name].each do |col|
|
350
|
+
return col.name if col.identity
|
351
|
+
end
|
352
|
+
|
353
|
+
return nil
|
354
|
+
end
|
355
|
+
|
356
|
+
def query_contains_identity_column(sql, col)
|
357
|
+
sql =~ /\[#{col}\]/
|
358
|
+
end
|
359
|
+
|
360
|
+
def change_order_direction(order)
|
361
|
+
order.split(",").collect {|fragment|
|
362
|
+
case fragment
|
363
|
+
when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
|
364
|
+
when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
|
365
|
+
else String.new(fragment).split(',').join(' DESC,') + ' DESC'
|
366
|
+
end
|
367
|
+
}.join(",")
|
368
|
+
end
|
369
|
+
|
370
|
+
def get_special_columns(table_name)
|
371
|
+
special = []
|
372
|
+
@table_columns ||= {}
|
373
|
+
@table_columns[table_name] ||= columns(table_name)
|
374
|
+
@table_columns[table_name].each do |col|
|
375
|
+
special << col.name if col.is_special
|
376
|
+
end
|
377
|
+
special
|
378
|
+
end
|
379
|
+
|
380
|
+
def repair_special_columns(sql)
|
381
|
+
special_cols = get_special_columns(get_table_name(sql))
|
382
|
+
for col in special_cols.to_a
|
383
|
+
sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
|
384
|
+
sql.gsub!(/ORDER BY #{col.to_s}/i, '')
|
385
|
+
end
|
386
|
+
sql
|
387
|
+
end
|
388
|
+
|
389
|
+
end #class MsSqlClientAdapter < AbstractAdapter
|
390
|
+
end #module ConnectionAdapters
|
391
|
+
end #module ActiveRecord
|