akitaonrails-activerecord-sqlserver-adapter 1.1.0 → 1.1.1
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/README +31 -0
- data/RUNNING_UNIT_TESTS +44 -0
- data/Rakefile +89 -0
- data/activerecord-sqlserver-adapter.gemspec +15 -0
- data/lib/activerecord-sqlserver-adapter.rb +3 -0
- data/lib/dbd/ADO.rb +229 -0
- data/lib/dbi.rb +1043 -0
- data/lib/dbi/columninfo.rb +158 -0
- data/lib/dbi/row.rb +205 -0
- data/lib/dbi/sql.rb +239 -0
- data/lib/dbi/trace.rb +90 -0
- data/lib/dbi/utils.rb +365 -0
- data/lib/dbi/version.rb +9 -0
- data/lib/rails_fcgi.rb +1 -0
- data/lib/rails_fcgi/fixes.rb +35 -0
- data/test/aaaa_create_tables_test_sqlserver.rb +43 -0
- data/test/affected_rows_test_sqlserver.rb +29 -0
- data/test/connections/native_sqlserver/connection.rb +23 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +35 -0
- data/test/fixtures/db_definitions/sqlserver.sql +247 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +4 -0
- metadata +36 -1
data/lib/dbi/trace.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# $Id: trace.rb,v 1.1 2006/01/04 02:03:22 francis Exp $
|
2
|
+
#
|
3
|
+
# Tracing for DBI programs
|
4
|
+
#
|
5
|
+
# Copyright (c) 2001 Michael Neumann
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License
|
9
|
+
# as published by the Free Software Foundation; either version 2
|
10
|
+
# of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
20
|
+
|
21
|
+
|
22
|
+
# works only correct with the newest version > 0.3.3
|
23
|
+
require "aspectr"
|
24
|
+
require "dbi" # to work as "ruby -r dbi/trace myapp.rb"
|
25
|
+
|
26
|
+
module DBI
|
27
|
+
|
28
|
+
class HandleTracer < AspectR::Aspect
|
29
|
+
|
30
|
+
def initialize(klass)
|
31
|
+
@never_wrap = /^__|^send$|^id$|^class$|^$ /
|
32
|
+
self.wrap(klass, :pre, :post, methods(klass))
|
33
|
+
end
|
34
|
+
|
35
|
+
# trace methods --------------------------------------------------------------
|
36
|
+
|
37
|
+
def pre(method, object, exitstatus, *args)
|
38
|
+
|
39
|
+
par = args.collect{|a| a.inspect}.join(", ")
|
40
|
+
|
41
|
+
if object.trace_mode == 2 then
|
42
|
+
object.trace_output << "-> #{method} for #{object} (#{par})\n"
|
43
|
+
elsif object.trace_mode == 3 then
|
44
|
+
object.trace_output << "-> #{method} for #{object.inspect} (#{par})\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def post(method, object, exitstatus, *args)
|
49
|
+
|
50
|
+
case object.trace_mode
|
51
|
+
when 1, 2 # return values and errors
|
52
|
+
arrow = object.trace_mode == 1 ? "<=" : "<-"
|
53
|
+
if exitstatus.kind_of? Array
|
54
|
+
object.trace_output << "#{arrow} #{method} for #{object} = #{exitstatus[0] || 'nil'}\n"
|
55
|
+
else
|
56
|
+
if exitstatus == true
|
57
|
+
object.trace_output << "!! #{$!.message.chomp}\n"
|
58
|
+
end
|
59
|
+
object.trace_output << "#{arrow} #{method} for #{object}\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
when 3
|
63
|
+
if exitstatus.kind_of? Array
|
64
|
+
object.trace_output << "<- #{method} for #{object.inspect} = #{exitstatus[0].inspect}\n"
|
65
|
+
else
|
66
|
+
if exitstatus == true
|
67
|
+
object.trace_output << "!! #{$!.inspect}\n"
|
68
|
+
end
|
69
|
+
object.trace_output << "<- #{method} for #{object.inspect}\n"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
private # helper methods -----------------------------------------------------
|
76
|
+
|
77
|
+
def methods(klass)
|
78
|
+
meths = (DBI::Handle.instance_methods | klass.instance_methods) - %w(trace_mode trace_output trace)
|
79
|
+
/(#{meths.collect{|m| Regexp.quote(m)}.join('|')})/
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
@@tracer_driver = HandleTracer.new(DBI::DriverHandle)
|
85
|
+
@@tracer_database = HandleTracer.new(DBI::DatabaseHandle)
|
86
|
+
@@tracer_statement = HandleTracer.new(DBI::StatementHandle)
|
87
|
+
|
88
|
+
|
89
|
+
end # module DBI
|
90
|
+
|
data/lib/dbi/utils.rb
ADDED
@@ -0,0 +1,365 @@
|
|
1
|
+
#
|
2
|
+
# $Id: utils.rb,v 1.5 2006/01/29 06:14:19 djberg96 Exp $
|
3
|
+
#
|
4
|
+
|
5
|
+
module DBI
|
6
|
+
class Date
|
7
|
+
attr_accessor :year, :month, :day
|
8
|
+
|
9
|
+
# DBI::Date.new(year = 0, month = 0, day = 0)
|
10
|
+
# DBI::Date.new(Date)
|
11
|
+
# DBI::Date.new(Time)
|
12
|
+
#
|
13
|
+
# Creates and returns a new DBI::Date object. It's similar to the
|
14
|
+
# standard Date class' constructor except that it also accepts a
|
15
|
+
# Date or Time object.
|
16
|
+
def initialize(year=0, month=0, day=0)
|
17
|
+
case year
|
18
|
+
when ::Date
|
19
|
+
@year, @month, @day = year.year, year.month, year.day
|
20
|
+
@original_date = year
|
21
|
+
when ::Time
|
22
|
+
@year, @month, @day = year.year, year.month, year.day
|
23
|
+
@original_time = year
|
24
|
+
else
|
25
|
+
@year, @month, @day = year, month, day
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Aliases
|
30
|
+
alias :mon :month
|
31
|
+
alias :mon= :month=
|
32
|
+
alias :mday :day
|
33
|
+
alias :mday= :day=
|
34
|
+
|
35
|
+
# Returns a new Time object based on the year, month and day or, if a
|
36
|
+
# Time object was passed to the constructor, returns that object.
|
37
|
+
def to_time
|
38
|
+
@original_time || ::Time.local(@year, @month, @day, 0, 0, 0)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a new Date object based on the year, month and day or, if a
|
42
|
+
# Date object was passed to the constructor, returns that object.
|
43
|
+
def to_date
|
44
|
+
@original_date || ::Date.new(@year, @month, @day)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a DBI::Date object as a string in YYYY-MM-DD format.
|
48
|
+
def to_s
|
49
|
+
sprintf("%04d-%02d-%02d", @year, @month, @day)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Time
|
54
|
+
attr_accessor :hour, :minute, :second
|
55
|
+
|
56
|
+
# DBI::Time.new(hour = 0, minute = 0, second = 0)
|
57
|
+
# DBI::Time.new(Time)
|
58
|
+
#
|
59
|
+
# Creates and returns a new DBI::Time object. Unlike the Time object
|
60
|
+
# in the standard library, accepts an hour, minute and second, or a
|
61
|
+
# Time object.
|
62
|
+
def initialize(hour=0, minute=0, second=0)
|
63
|
+
case hour
|
64
|
+
when ::Time
|
65
|
+
@hour, @minute, @second = hour.hour, hour.min, hour.sec
|
66
|
+
@original_time = hour
|
67
|
+
else
|
68
|
+
@hour, @minute, @second = hour, minute, second
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
alias :min :minute
|
73
|
+
alias :min= :minute=
|
74
|
+
alias :sec :second
|
75
|
+
alias :sec= :second=
|
76
|
+
|
77
|
+
# Returns a new Time object based on the hour, minute and second, using
|
78
|
+
# the current year, month and day. If a Time object was passed to the
|
79
|
+
# constructor, returns that object instead.
|
80
|
+
def to_time
|
81
|
+
if @original_time
|
82
|
+
@original_time
|
83
|
+
else
|
84
|
+
t = ::Time.now
|
85
|
+
::Time.local(t.year, t.month, t.day, @hour, @minute, @second)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a DBI::Time object as a string in HH:MM:SS format.
|
90
|
+
def to_s
|
91
|
+
sprintf("%02d:%02d:%02d", @hour, @minute, @second)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Timestamp
|
96
|
+
attr_accessor :year, :month, :day
|
97
|
+
attr_accessor :hour, :minute, :second
|
98
|
+
attr_writer :fraction
|
99
|
+
|
100
|
+
# DBI::Timestamp(year=0,month=0,day=0,hour=0,min=0,sec=0,fraction=nil)
|
101
|
+
# DBI::Timestamp(Time)
|
102
|
+
# DBI::Timestamp(Date)
|
103
|
+
#
|
104
|
+
# Creates and returns a new DBI::Timestamp object. This is similar to
|
105
|
+
# a Time object in the standard library, but it also contains fractional
|
106
|
+
# seconds, expressed in nanoseconds. In addition, the constructor
|
107
|
+
# accepts either a Date or Time object.
|
108
|
+
def initialize(year=0, month=0, day=0, hour=0, min=0, sec=0, fraction=nil)
|
109
|
+
case year
|
110
|
+
when ::Time
|
111
|
+
@year, @month, @day = year.year, year.month, year.day
|
112
|
+
@hour, @minute, @second, @fraction = year.hour, year.min, year.sec, nil
|
113
|
+
@original_time = year
|
114
|
+
when ::Date
|
115
|
+
@year, @month, @day = year.year, year.month, year.day
|
116
|
+
@hour, @minute, @second, @fraction = 0, 0, 0, nil
|
117
|
+
@original_date = year
|
118
|
+
else
|
119
|
+
@year, @month, @day = year, month, day
|
120
|
+
@hour, @minute, @second, @fraction = hour, min, sec, fraction
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns true if +timestamp+ has a year, month, day, hour, minute,
|
125
|
+
# second and fraction equal to the comparing object.
|
126
|
+
#
|
127
|
+
# Returns false if the comparison fails for any reason.
|
128
|
+
def ==(timestamp)
|
129
|
+
@year == timestamp.year and @month == timestamp.month and
|
130
|
+
@day == timestamp.day and @hour == timestamp.hour and
|
131
|
+
@minute == timestamp.minute and @second == timestamp.second and
|
132
|
+
(fraction() == timestamp.fraction)
|
133
|
+
rescue
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns fractional seconds, or 0 if not set.
|
138
|
+
def fraction
|
139
|
+
@fraction || 0
|
140
|
+
end
|
141
|
+
|
142
|
+
# Aliases
|
143
|
+
alias :mon :month
|
144
|
+
alias :mon= :month=
|
145
|
+
alias :mday :day
|
146
|
+
alias :mday= :day=
|
147
|
+
alias :min :minute
|
148
|
+
alias :min= :minute=
|
149
|
+
alias :sec :second
|
150
|
+
alias :sec= :second=
|
151
|
+
|
152
|
+
# Returns a DBI::Timestamp object as a string in YYYY-MM-DD HH:MM:SS
|
153
|
+
# format. If a fraction is present, then it is appended in ".FF" format.
|
154
|
+
def to_s
|
155
|
+
string = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
|
156
|
+
@year, @month, @day, @hour, @minute, @second)
|
157
|
+
|
158
|
+
if @fraction
|
159
|
+
fraction = ("%.9f" % (@fraction.to_i / 1e9)).
|
160
|
+
to_s[1..-1].gsub(/0{1,8}$/, "")
|
161
|
+
string += fraction
|
162
|
+
end
|
163
|
+
|
164
|
+
string
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns a new Time object based on the year, month and day or, if a
|
168
|
+
# Time object was passed to the constructor, returns that object.
|
169
|
+
def to_time
|
170
|
+
@original_time || ::Time.local(@year, @month, @day, @hour, @minute, @second)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns a new Date object based on the year, month and day or, if a
|
174
|
+
# Date object was passed to the constructor, returns that object.
|
175
|
+
def to_date
|
176
|
+
@original_date || ::Date.new(@year, @month, @day)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module Utils
|
181
|
+
|
182
|
+
module ConvParam
|
183
|
+
def conv_param(*params)
|
184
|
+
params.collect do |p|
|
185
|
+
case p
|
186
|
+
when ::Date
|
187
|
+
DBI::Date.new(p)
|
188
|
+
when ::Time
|
189
|
+
DBI::Timestamp.new(p)
|
190
|
+
else
|
191
|
+
p
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def Utils.measure
|
199
|
+
start = ::Time.now
|
200
|
+
yield
|
201
|
+
::Time.now - start
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# parse a string of the form "database=xxx;key=val;..."
|
206
|
+
# or database:host and return hash of key/value pairs
|
207
|
+
#
|
208
|
+
# improved by John Gorman <jgorman@webbysoft.com>
|
209
|
+
def Utils.parse_params(str)
|
210
|
+
params = str.split(";")
|
211
|
+
hash = {}
|
212
|
+
params.each do |param|
|
213
|
+
key, val = param.split("=")
|
214
|
+
hash[key] = val if key and val
|
215
|
+
end
|
216
|
+
if hash.empty?
|
217
|
+
database, host = str.split(":")
|
218
|
+
hash['database'] = database if database
|
219
|
+
hash['host'] = host if host
|
220
|
+
end
|
221
|
+
hash
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
module XMLFormatter
|
226
|
+
def XMLFormatter.row(dbrow, rowtag="row", output=STDOUT)
|
227
|
+
#XMLFormatter.extended_row(dbrow, "row", [],
|
228
|
+
output << "<#{rowtag}>\n"
|
229
|
+
dbrow.each_with_name do |val, name|
|
230
|
+
output << " <#{name}>" + textconv(val) + "</#{name}>\n"
|
231
|
+
end
|
232
|
+
output << "</#{rowtag}>\n"
|
233
|
+
end
|
234
|
+
|
235
|
+
# nil in cols_as_tag, means "all columns expect those listed in cols_in_row_tag"
|
236
|
+
# add_row_tag_attrs are additional attributes which are inserted into the row-tag
|
237
|
+
def XMLFormatter.extended_row(dbrow, rowtag="row", cols_in_row_tag=[], cols_as_tag=nil, add_row_tag_attrs={}, output=STDOUT)
|
238
|
+
if cols_as_tag.nil?
|
239
|
+
cols_as_tag = dbrow.column_names - cols_in_row_tag
|
240
|
+
end
|
241
|
+
|
242
|
+
output << "<#{rowtag}"
|
243
|
+
add_row_tag_attrs.each do |key, val|
|
244
|
+
# TODO: use textconv ? " substitution?
|
245
|
+
output << %{ #{key}="#{textconv(val)}"}
|
246
|
+
end
|
247
|
+
cols_in_row_tag.each do |key|
|
248
|
+
# TODO: use textconv ? " substitution?
|
249
|
+
output << %{ #{key}="#{dbrow[key]}"}
|
250
|
+
end
|
251
|
+
output << ">\n"
|
252
|
+
|
253
|
+
cols_as_tag.each do |key|
|
254
|
+
output << " <#{key}>" + textconv(dbrow[key]) + "</#{key}>\n"
|
255
|
+
end
|
256
|
+
output << "</#{rowtag}>\n"
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
|
261
|
+
def XMLFormatter.table(rows, roottag = "rows", rowtag = "row", output=STDOUT)
|
262
|
+
output << '<?xml version="1.0" encoding="UTF-8" ?>'
|
263
|
+
output << "\n<#{roottag}>\n"
|
264
|
+
rows.each do |row|
|
265
|
+
row(row, rowtag, output)
|
266
|
+
end
|
267
|
+
output << "</#{roottag}>\n"
|
268
|
+
end
|
269
|
+
|
270
|
+
class << self
|
271
|
+
private
|
272
|
+
# from xmloracle.rb
|
273
|
+
def textconv(str)
|
274
|
+
str = str.to_s.gsub('&', "&")
|
275
|
+
str = str.gsub('\'', "'")
|
276
|
+
str = str.gsub('"', """)
|
277
|
+
str = str.gsub('<', "<")
|
278
|
+
str.gsub('>', ">")
|
279
|
+
end
|
280
|
+
end # class self
|
281
|
+
|
282
|
+
end # module XMLFormatter
|
283
|
+
|
284
|
+
|
285
|
+
module TableFormatter
|
286
|
+
|
287
|
+
# TODO: add a nr-column where the number of the column is shown
|
288
|
+
def TableFormatter.ascii(header, rows,
|
289
|
+
header_orient=:left, rows_orient=:left,
|
290
|
+
indent=2, cellspace=1, pagebreak_after=nil,
|
291
|
+
output=STDOUT)
|
292
|
+
|
293
|
+
header_orient ||= :left
|
294
|
+
rows_orient ||= :left
|
295
|
+
indent ||= 2
|
296
|
+
cellspace ||= 1
|
297
|
+
|
298
|
+
# pagebreak_after n-rows (without counting header or split-lines)
|
299
|
+
# yield block with output as param after each pagebreak (not at the end)
|
300
|
+
|
301
|
+
col_lengths = (0...(header.size)).collect do |colnr|
|
302
|
+
[
|
303
|
+
(0...rows.size).collect { |rownr|
|
304
|
+
value = rows[rownr][colnr]
|
305
|
+
(value.nil? ? "NULL" : value).to_s.size
|
306
|
+
}.max,
|
307
|
+
header[colnr].size
|
308
|
+
].max
|
309
|
+
end
|
310
|
+
|
311
|
+
indent = " " * indent
|
312
|
+
|
313
|
+
split_line = indent + "+"
|
314
|
+
col_lengths.each {|col| split_line << "-" * (col+cellspace*2) + "+" }
|
315
|
+
|
316
|
+
cellspace = " " * cellspace
|
317
|
+
|
318
|
+
output_row = proc {|row, orient|
|
319
|
+
output << indent + "|"
|
320
|
+
row.each_with_index {|c,i|
|
321
|
+
output << cellspace
|
322
|
+
str = (c.nil? ? "NULL" : c).to_s
|
323
|
+
output << case orient
|
324
|
+
when :left then str.ljust(col_lengths[i])
|
325
|
+
when :right then str.rjust(col_lengths[i])
|
326
|
+
when :center then str.center(col_lengths[i])
|
327
|
+
end
|
328
|
+
output << cellspace
|
329
|
+
output << "|"
|
330
|
+
}
|
331
|
+
output << "\n"
|
332
|
+
}
|
333
|
+
|
334
|
+
rownr = 0
|
335
|
+
|
336
|
+
loop do
|
337
|
+
output << split_line + "\n"
|
338
|
+
output_row.call(header, header_orient)
|
339
|
+
output << split_line + "\n"
|
340
|
+
if pagebreak_after.nil?
|
341
|
+
rows.each {|ar| output_row.call(ar, rows_orient)}
|
342
|
+
output << split_line + "\n"
|
343
|
+
break
|
344
|
+
end
|
345
|
+
|
346
|
+
rows[rownr,pagebreak_after].each {|ar| output_row.call(ar, rows_orient)}
|
347
|
+
output << split_line + "\n"
|
348
|
+
|
349
|
+
rownr += pagebreak_after
|
350
|
+
|
351
|
+
break if rownr >= rows.size
|
352
|
+
|
353
|
+
yield output if block_given?
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
|
360
|
+
end # module TableFormatter
|
361
|
+
|
362
|
+
end # module Utils
|
363
|
+
end # module DBI
|
364
|
+
|
365
|
+
|