akitaonrails-activerecord-sqlserver-adapter 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,158 @@
|
|
1
|
+
class ColumnInfo < Hash
|
2
|
+
|
3
|
+
# Creates and returns a ColumnInfo object. This represents metadata for
|
4
|
+
# columns within a given table, such as the data type, whether or not the
|
5
|
+
# the column is a primary key, etc.
|
6
|
+
#
|
7
|
+
# ColumnInfo is a subclass of Hash.
|
8
|
+
#
|
9
|
+
def initialize(hash=nil)
|
10
|
+
self.update(hash) if hash
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the column's name.
|
14
|
+
def name
|
15
|
+
self['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sets the column's name.
|
19
|
+
def name=(val)
|
20
|
+
self['name'] = val
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a portable integer representation of the column's type. Here are
|
24
|
+
# the constant names (under DBI) and their respective values:
|
25
|
+
#
|
26
|
+
# SQL_CHAR = 1
|
27
|
+
# SQL_NUMERIC = 2
|
28
|
+
# SQL_DECIMAL = 3
|
29
|
+
# SQL_INTEGER = 4
|
30
|
+
# SQL_SMALLINT = 5
|
31
|
+
# SQL_FLOAT = 6
|
32
|
+
# SQL_REAL = 7
|
33
|
+
# SQL_DOUBLE = 8
|
34
|
+
# SQL_DATE = 9
|
35
|
+
# SQL_TIME = 10
|
36
|
+
# SQL_TIMESTAMP = 11
|
37
|
+
# SQL_VARCHAR = 12
|
38
|
+
#
|
39
|
+
# SQL_LONGVARCHAR = -1
|
40
|
+
# SQL_BINARY = -2
|
41
|
+
# SQL_VARBINARY = -3
|
42
|
+
# SQL_LONGVARBINARY = -4
|
43
|
+
# SQL_BIGINT = -5
|
44
|
+
# SQL_BIT = -7
|
45
|
+
# SQL_TINYINT = -6
|
46
|
+
#
|
47
|
+
def sql_type
|
48
|
+
self['sql_type']
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the integer representation for the column's type.
|
52
|
+
def sql_type=(val)
|
53
|
+
self['sql_type'] = val
|
54
|
+
end
|
55
|
+
|
56
|
+
# A string representation of the column's type, e.g. 'date'.
|
57
|
+
def type_name
|
58
|
+
self['type_name']
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets the representation for the column's type.
|
62
|
+
def type_name=(val)
|
63
|
+
self['type_name'] = val
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the precision, i.e. number of bytes or digits.
|
67
|
+
def precision
|
68
|
+
self['precision']
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets the precision, i.e. number of bytes or digits.
|
72
|
+
def precision=(val)
|
73
|
+
self['precision'] = val
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the number of digits from right.
|
77
|
+
def scale
|
78
|
+
self['scale']
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sets the number of digits from right.
|
82
|
+
def scale=(val)
|
83
|
+
self['scale'] = val
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the default value for the column, or nil if not set.
|
87
|
+
def default
|
88
|
+
self['default']
|
89
|
+
end
|
90
|
+
|
91
|
+
# Sets the default value for the column.
|
92
|
+
def default=(val)
|
93
|
+
self['default'] = val
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns whether or not the column is may contain a NULL.
|
97
|
+
def nullable
|
98
|
+
self['nullable']
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sets whether or not the column may contain a NULL.
|
102
|
+
def nullable=(val)
|
103
|
+
self['nullable'] = val
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns whether or not the column is indexed.
|
107
|
+
def indexed
|
108
|
+
self['indexed']
|
109
|
+
end
|
110
|
+
|
111
|
+
# Sets whether or not the column is indexed.
|
112
|
+
def indexed=(val)
|
113
|
+
self['indexed'] = 'val'
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns whether or not the column is a primary key.
|
117
|
+
def primary
|
118
|
+
self['primary']
|
119
|
+
end
|
120
|
+
|
121
|
+
# Sets whether or not the column is a primary key.
|
122
|
+
def primary=(val)
|
123
|
+
self['primary'] = val
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns whether or not data in the column must be unique.
|
127
|
+
def unique
|
128
|
+
self['unique']
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets whether or not data in the column must be unique.
|
132
|
+
def unique=(val)
|
133
|
+
self['unique'] = val
|
134
|
+
end
|
135
|
+
|
136
|
+
# Aliases
|
137
|
+
alias nullable? nullable
|
138
|
+
alias is_nullable? nullable
|
139
|
+
|
140
|
+
alias indexed? indexed
|
141
|
+
alias is_indexed? indexed
|
142
|
+
|
143
|
+
alias primary? primary
|
144
|
+
alias is_primary? primary
|
145
|
+
|
146
|
+
alias unique? unique
|
147
|
+
alias is_unique unique
|
148
|
+
|
149
|
+
alias size precision
|
150
|
+
alias size= precision=
|
151
|
+
alias length precision
|
152
|
+
alias length= precision=
|
153
|
+
|
154
|
+
alias decimal_digits scale
|
155
|
+
alias decimal_digits= scale=
|
156
|
+
end
|
157
|
+
|
158
|
+
|
data/lib/dbi/row.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
# The DBI::Row class is a delegate of Array, rather than a subclass, because
|
4
|
+
# there are times when it should act like an Array, and others when it should
|
5
|
+
# act like a Hash (and still others where it should act like String, Regexp,
|
6
|
+
# etc). It also needs to store metadata about the row, such as
|
7
|
+
# column data type and index information, that users can then access.
|
8
|
+
#
|
9
|
+
module DBI
|
10
|
+
class Row < DelegateClass(Array)
|
11
|
+
attr_reader :column_names
|
12
|
+
|
13
|
+
# DBI::Row.new(columns, size_or_array=nil)
|
14
|
+
#
|
15
|
+
# Returns a new DBI::Row object using +columns+. The +size_or_array+
|
16
|
+
# argument may either be an Integer or an Array. If it is not provided,
|
17
|
+
# it defaults to the length of +columns+.
|
18
|
+
#
|
19
|
+
# DBI::Row is a delegate of the Array class, so all of the Array
|
20
|
+
# instance methods are available to your DBI::Row object (keeping in
|
21
|
+
# mind that initialize, [], and []= have been explicitly overridden).
|
22
|
+
#
|
23
|
+
def initialize(columns, size_or_array=nil)
|
24
|
+
size_or_array ||= columns.size
|
25
|
+
|
26
|
+
case size_or_array
|
27
|
+
when Integer
|
28
|
+
@arr = Array.new(size_or_array)
|
29
|
+
when Array
|
30
|
+
@arr = size_or_array
|
31
|
+
else
|
32
|
+
raise TypeError, "parameter must be either Integer or Array"
|
33
|
+
end
|
34
|
+
|
35
|
+
# The '@column_map' is used to map column names to integer values so
|
36
|
+
# that users can reference row values by name or number.
|
37
|
+
|
38
|
+
@column_map = {}
|
39
|
+
@column_names = columns
|
40
|
+
columns.each_with_index { |c,i| @column_map[c] = i }
|
41
|
+
super(@arr)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Replaces the contents of @arr with +new_values+
|
45
|
+
def set_values(new_values)
|
46
|
+
@arr.replace(new_values)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Yields a column value by name (rather than index), along with the
|
50
|
+
# column name itself.
|
51
|
+
def each_with_name
|
52
|
+
@arr.each_with_index do |v, i|
|
53
|
+
yield v, @column_names[i]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the Row object as a hash
|
58
|
+
def to_h
|
59
|
+
hash = {}
|
60
|
+
each_with_name{ |v, n| hash[n] = v}
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create a new row with 'new_values', reusing the field name hash.
|
65
|
+
def clone_with(new_values)
|
66
|
+
Row.new(@column_names, new_values)
|
67
|
+
end
|
68
|
+
|
69
|
+
alias field_names column_names
|
70
|
+
|
71
|
+
# Retrieve a value by index (rather than name).
|
72
|
+
#
|
73
|
+
# Deprecated. Since Row delegates to Array, just use Row#at.
|
74
|
+
def by_index(index)
|
75
|
+
@arr[index]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Value of the field named +field_name+ or nil if not found.
|
79
|
+
def by_field(field_name)
|
80
|
+
begin
|
81
|
+
@arr[@column_map[field_name.to_s]]
|
82
|
+
rescue TypeError
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Row#[]
|
88
|
+
#
|
89
|
+
# row[int]
|
90
|
+
# row[array]
|
91
|
+
# row[regexp]
|
92
|
+
# row[arg, arg]
|
93
|
+
# row[arg, arg, ...]
|
94
|
+
#
|
95
|
+
# Sample: Row.new(["first","last","age"], ["Daniel", "Berger", "36"])
|
96
|
+
#
|
97
|
+
# Retrieves row elements. Exactly what it retrieves depends on the
|
98
|
+
# kind and number of arguments used.
|
99
|
+
#
|
100
|
+
# Zero arguments will raise an ArgumentError.
|
101
|
+
#
|
102
|
+
# One argument will return a single result. This can be a String,
|
103
|
+
# Symbol, Integer, Range or Regexp and the appropriate result will
|
104
|
+
# be returned. Strings, Symbols and Regexps act like hash lookups,
|
105
|
+
# while Integers and Ranges act like Array index lookups.
|
106
|
+
#
|
107
|
+
# Two arguments will act like the second form of Array#[], i.e it takes
|
108
|
+
# two integers, with the first number the starting point and the second
|
109
|
+
# number the length, and returns an array of values.
|
110
|
+
#
|
111
|
+
# If three or more arguments are provided, an array of results is
|
112
|
+
# returned. The behavior for each argument is that of a single argument,
|
113
|
+
# i.e. Strings, Symbols, and Regexps act like hash lookups, while
|
114
|
+
# Integers and Ranges act like Array index lookups.
|
115
|
+
#
|
116
|
+
# If no results are found, or an unhandled type is passed, then nil
|
117
|
+
# (or a nil element) is returned.
|
118
|
+
#
|
119
|
+
def [](*args)
|
120
|
+
begin
|
121
|
+
case args.length
|
122
|
+
when 0
|
123
|
+
err = "wrong # of arguments(#{args.size} for at least 1)"
|
124
|
+
raise ArgumentError, err
|
125
|
+
when 1
|
126
|
+
case args[0]
|
127
|
+
when Array
|
128
|
+
args[0].collect { |e| self[e] }
|
129
|
+
when Regexp
|
130
|
+
self[@column_names.grep(args[0])]
|
131
|
+
else
|
132
|
+
@arr[conv_param(args[0])]
|
133
|
+
end
|
134
|
+
# We explicitly check for a length of 2 in order to properly
|
135
|
+
# simulate the second form of Array#[].
|
136
|
+
when 2
|
137
|
+
@arr[conv_param(args[0]), conv_param(args[1])]
|
138
|
+
else
|
139
|
+
results = []
|
140
|
+
args.flatten.each{ |arg|
|
141
|
+
case arg
|
142
|
+
when Integer
|
143
|
+
results.push(@arr[arg])
|
144
|
+
when Regexp
|
145
|
+
results.push(self[@column_names.grep(arg)])
|
146
|
+
else
|
147
|
+
results.push(self[conv_param(arg)])
|
148
|
+
end
|
149
|
+
}
|
150
|
+
results.flatten
|
151
|
+
end
|
152
|
+
rescue TypeError
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Assign a value to a Row object by element. You can assign using
|
158
|
+
# a single element reference, or by using a start and length similar
|
159
|
+
# to the second form of Array#[]=.
|
160
|
+
#
|
161
|
+
# row[0] = "kirk"
|
162
|
+
# row[:last] = "haines"
|
163
|
+
# row[0, 2] = "test"
|
164
|
+
#
|
165
|
+
def []=(key, value_or_length, obj=nil)
|
166
|
+
if obj
|
167
|
+
@arr[conv_param(key), conv_param(value_or_length)] = obj
|
168
|
+
else
|
169
|
+
@arr[conv_param(key)] = value_or_length
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def clone
|
174
|
+
clone_with(@arr.dup)
|
175
|
+
end
|
176
|
+
|
177
|
+
alias dup clone
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
# Simple helper method to grab the proper value from @column_map
|
182
|
+
def conv_param(arg)
|
183
|
+
case arg
|
184
|
+
when String, Symbol
|
185
|
+
@column_map[arg.to_s]
|
186
|
+
when Range
|
187
|
+
if arg.first.kind_of?(Symbol) || arg.first.kind_of?(String)
|
188
|
+
first = @column_map[arg.first.to_s]
|
189
|
+
last = @column_map[arg.last.to_s]
|
190
|
+
else
|
191
|
+
first = arg.first
|
192
|
+
last = arg.last
|
193
|
+
end
|
194
|
+
|
195
|
+
if arg.exclude_end?
|
196
|
+
(first...last)
|
197
|
+
else
|
198
|
+
(first..last)
|
199
|
+
end
|
200
|
+
else
|
201
|
+
arg
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end # class Row
|
205
|
+
end # module DBI
|
data/lib/dbi/sql.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
#
|
2
|
+
# $Id: sql.rb,v 1.3 2006/03/27 20:25:02 francis Exp $
|
3
|
+
#
|
4
|
+
# parts extracted from Jim Weirichs DBD::Pg
|
5
|
+
#
|
6
|
+
|
7
|
+
module DBI
|
8
|
+
require "dbi/utils"
|
9
|
+
require "parsedate"
|
10
|
+
require "time"
|
11
|
+
|
12
|
+
module SQL
|
13
|
+
|
14
|
+
## Is the SQL statement a query?
|
15
|
+
def SQL.query?(sql)
|
16
|
+
sql =~ /^\s*select\b/i
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
####################################################################
|
21
|
+
# Mixin module useful for expanding SQL statements.
|
22
|
+
#
|
23
|
+
module BasicQuote
|
24
|
+
|
25
|
+
# by Masatoshi SEKI
|
26
|
+
class Coerce
|
27
|
+
def as_int(str)
|
28
|
+
return nil if str.nil?
|
29
|
+
if str == "" then nil else str.to_i end
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_float(str)
|
33
|
+
return nil if str.nil?
|
34
|
+
str.to_f
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_str(str)
|
38
|
+
str
|
39
|
+
end
|
40
|
+
|
41
|
+
def as_bool(str)
|
42
|
+
if str == "t" or str == "1"
|
43
|
+
true
|
44
|
+
elsif str == "f" or str == "0"
|
45
|
+
false
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def as_time(str)
|
52
|
+
return nil if str.nil? or str.empty?
|
53
|
+
t = ParseDate.parsedate(str)
|
54
|
+
DBI::Time.new(*t[3,3])
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def as_timestamp(str)
|
59
|
+
return nil if str.nil? or str.empty?
|
60
|
+
ary = ParseDate.parsedate(str)
|
61
|
+
begin
|
62
|
+
time = ::Time.gm(*(ary[0,6]))
|
63
|
+
rescue ArgumentError => ae
|
64
|
+
# don't fault stupid values that MySQL nevertheless stores
|
65
|
+
return nil
|
66
|
+
end
|
67
|
+
if ary[6] =~ /^((\+|\-)\d+)(:\d+)?$/
|
68
|
+
diff = $1.to_i * 3600 # seconds per hour
|
69
|
+
time -= diff
|
70
|
+
time.localtime
|
71
|
+
end
|
72
|
+
DBI::Timestamp.new(time)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def as_date(str)
|
77
|
+
return nil if str.nil?
|
78
|
+
ary = ParseDate.parsedate(str)
|
79
|
+
DBI::Date.new(*ary[0,3])
|
80
|
+
rescue
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def coerce(sym, str)
|
86
|
+
self.send(sym, str)
|
87
|
+
end
|
88
|
+
|
89
|
+
end # class Coerce
|
90
|
+
|
91
|
+
|
92
|
+
## Quote strings appropriately for SQL statements
|
93
|
+
def quote(value)
|
94
|
+
case value
|
95
|
+
when String
|
96
|
+
value = value.gsub(/'/, "''") # ' (for ruby-mode)
|
97
|
+
"'#{value}'"
|
98
|
+
when NilClass
|
99
|
+
"NULL"
|
100
|
+
when TrueClass
|
101
|
+
"'t'"
|
102
|
+
when FalseClass
|
103
|
+
"'f'"
|
104
|
+
when Array
|
105
|
+
value.collect { |v| quote(v) }.join(", ")
|
106
|
+
when DBI::Date, DBI::Time, DBI::Timestamp, ::Date
|
107
|
+
"'#{value.to_s}'"
|
108
|
+
when ::Time
|
109
|
+
"'#{value.rfc2822}'"
|
110
|
+
else
|
111
|
+
value.to_s
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end # module BasicQuote
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
####################################################################
|
119
|
+
# Mixin module useful for binding arguments to an SQL string.
|
120
|
+
#
|
121
|
+
module BasicBind
|
122
|
+
|
123
|
+
## Bind the :sql string to an array of :args, quoting with :quoter.
|
124
|
+
#
|
125
|
+
def bind(quoter, sql, args)
|
126
|
+
arg_index = 0
|
127
|
+
result = ""
|
128
|
+
tokens(sql).each { |part|
|
129
|
+
case part
|
130
|
+
when '?'
|
131
|
+
result << quoter.quote(args[arg_index])
|
132
|
+
arg_index += 1
|
133
|
+
when '??'
|
134
|
+
result << "?"
|
135
|
+
else
|
136
|
+
result << part
|
137
|
+
end
|
138
|
+
}
|
139
|
+
if arg_index < args.size
|
140
|
+
raise "Too many SQL parameters"
|
141
|
+
elsif arg_index > args.size
|
142
|
+
raise "Not enough SQL parameters"
|
143
|
+
end
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
## Break the sql string into parts.
|
148
|
+
#
|
149
|
+
# This is NOT a full lexer for SQL. It just breaks up the SQL
|
150
|
+
# string enough so that question marks, double question marks and
|
151
|
+
# quoted strings are separated. This is used when binding
|
152
|
+
# arguments to "?" in the SQL string.
|
153
|
+
#
|
154
|
+
# C-style (/* */) and Ada-style (--) comments are handled.
|
155
|
+
# Note: Nested C-style comments are NOT handled!
|
156
|
+
#
|
157
|
+
def tokens(sql)
|
158
|
+
sql.scan(%r{
|
159
|
+
(
|
160
|
+
-- .* (?# matches "--" style comments to the end of line or string )
|
161
|
+
| - (?# matches single "-" )
|
162
|
+
|
|
163
|
+
/[*] .*? [*]/ (?# matches C-style comments )
|
164
|
+
| / (?# matches single slash )
|
165
|
+
|
|
166
|
+
' ( [^'\\] | '' | \\. )* ' (?# match strings surrounded by apostophes )
|
167
|
+
|
|
168
|
+
" ( [^"\\] | "" | \\. )* " (?# match strings surrounded by " )
|
169
|
+
|
|
170
|
+
\?\?? (?# match one or two question marks )
|
171
|
+
|
|
172
|
+
[^-/'"?]+ (?# match all characters except ' " ? - and / )
|
173
|
+
|
174
|
+
)}x).collect {|t| t.first}
|
175
|
+
end
|
176
|
+
|
177
|
+
end # module BasicBind
|
178
|
+
|
179
|
+
|
180
|
+
class PreparedStatement
|
181
|
+
include BasicBind # for method tokens(sql)
|
182
|
+
|
183
|
+
attr_accessor :unbound
|
184
|
+
|
185
|
+
def initialize(quoter, sql)
|
186
|
+
@quoter, @sql = quoter, sql
|
187
|
+
prepare
|
188
|
+
end
|
189
|
+
|
190
|
+
def bind(args)
|
191
|
+
if @arg_index < args.size
|
192
|
+
raise "Too many SQL parameters"
|
193
|
+
elsif @arg_index > args.size
|
194
|
+
raise "Not enough SQL parameters"
|
195
|
+
end
|
196
|
+
|
197
|
+
@unbound.each do |res_pos, arg_pos|
|
198
|
+
@result[res_pos] = @quoter.quote(args[arg_pos])
|
199
|
+
end
|
200
|
+
|
201
|
+
@result.join("")
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
def prepare
|
207
|
+
@result = []
|
208
|
+
@unbound = {}
|
209
|
+
pos = 0
|
210
|
+
@arg_index = 0
|
211
|
+
|
212
|
+
tokens(@sql).each { |part|
|
213
|
+
case part
|
214
|
+
when '?'
|
215
|
+
@result[pos] = nil
|
216
|
+
@unbound[pos] = @arg_index
|
217
|
+
pos += 1
|
218
|
+
@arg_index += 1
|
219
|
+
when '??'
|
220
|
+
if @result[pos-1] != nil
|
221
|
+
@result[pos-1] << "?"
|
222
|
+
else
|
223
|
+
@result[pos] = "?"
|
224
|
+
pos += 1
|
225
|
+
end
|
226
|
+
else
|
227
|
+
if @result[pos-1] != nil
|
228
|
+
@result[pos-1] << part
|
229
|
+
else
|
230
|
+
@result[pos] = part
|
231
|
+
pos += 1
|
232
|
+
end
|
233
|
+
end
|
234
|
+
}
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
end # module SQL
|
239
|
+
end # module DBI
|