mssqlclient 0.1.0
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 +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
|