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
@@ -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
|