Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/activerecord/lib/active_record/vendor/db2.rb

Revision 3655, 10.4 kB (checked in by david, 3 years ago)

Fixed db2 connection with empty user_name and auth options (closes #3622) [phurley@gmail.com]

Line 
1 require 'db2/db2cli.rb'
2
3 module DB2
4   module DB2Util
5     include DB2CLI
6
7     def free() SQLFreeHandle(@handle_type, @handle); end
8     def handle() @handle; end
9
10     def check_rc(rc)
11       if ![SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA_FOUND].include?(rc)
12         rec = 1
13         msg = ''
14         loop do
15           a = SQLGetDiagRec(@handle_type, @handle, rec, 500)
16           break if a[0] != SQL_SUCCESS
17           msg << a[3] if !a[3].nil? and a[3] != '' # Create message.
18           rec += 1
19         end
20         raise "DB2 error: #{msg}"
21       end
22     end
23   end
24
25   class Environment
26     include DB2Util
27
28     def initialize
29       @handle_type = SQL_HANDLE_ENV
30       rc, @handle = SQLAllocHandle(@handle_type, SQL_NULL_HANDLE)
31       check_rc(rc)
32     end
33
34     def data_sources(buffer_length = 1024)
35       retval = []
36       max_buffer_length = buffer_length
37
38       a = SQLDataSources(@handle, SQL_FETCH_FIRST, SQL_MAX_DSN_LENGTH + 1, buffer_length)
39       retval << [a[1], a[3]]
40       max_buffer_length = [max_buffer_length, a[4]].max
41
42       loop do
43         a = SQLDataSources(@handle, SQL_FETCH_NEXT, SQL_MAX_DSN_LENGTH + 1, buffer_length)
44         break if a[0] == SQL_NO_DATA_FOUND
45
46         retval << [a[1], a[3]]
47         max_buffer_length = [max_buffer_length, a[4]].max
48       end
49
50       if max_buffer_length > buffer_length
51         get_data_sources(max_buffer_length)
52       else
53         retval
54       end
55     end
56   end
57
58   class Connection
59     include DB2Util
60
61     def initialize(environment)
62       @env = environment
63       @handle_type = SQL_HANDLE_DBC
64       rc, @handle = SQLAllocHandle(@handle_type, @env.handle)
65       check_rc(rc)
66     end
67
68     def connect(server_name, user_name = '', auth = '')
69       check_rc(SQLConnect(@handle, server_name, user_name.to_s, auth.to_s))
70     end
71
72     def set_connect_attr(attr, value)
73       value += "\0" if value.class == String
74       check_rc(SQLSetConnectAttr(@handle, attr, value))
75     end
76
77     def set_auto_commit_on
78       set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON)
79     end
80
81     def set_auto_commit_off
82       set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF)
83     end
84
85     def disconnect
86       check_rc(SQLDisconnect(@handle))
87     end
88
89     def rollback
90       check_rc(SQLEndTran(@handle_type, @handle, SQL_ROLLBACK))
91     end
92
93     def commit
94       check_rc(SQLEndTran(@handle_type, @handle, SQL_COMMIT))
95     end
96   end
97
98   class Statement
99     include DB2Util
100
101     def initialize(connection)
102       @conn = connection
103       @handle_type = SQL_HANDLE_STMT
104       @parms = []                           #yun
105       @sql = ''                             #yun
106       @numParms = 0                         #yun
107       @prepared = false                     #yun
108       @parmArray = []                       #yun. attributes of the parameter markers
109       rc, @handle = SQLAllocHandle(@handle_type, @conn.handle)
110       check_rc(rc)
111     end
112
113     def columns(table_name, schema_name = '%')
114       check_rc(SQLColumns(@handle, '', schema_name.upcase, table_name.upcase, '%'))
115       fetch_all
116     end
117
118     def tables(schema_name = '%')
119       check_rc(SQLTables(@handle, '', schema_name.upcase, '%', 'TABLE'))
120       fetch_all
121     end
122
123     def indexes(table_name, schema_name = '')
124       check_rc(SQLStatistics(@handle, '', schema_name.upcase, table_name.upcase, SQL_INDEX_ALL, SQL_ENSURE))
125       fetch_all
126     end
127
128     def prepare(sql)
129       @sql = sql
130       check_rc(SQLPrepare(@handle, sql))
131       rc, @numParms = SQLNumParams(@handle) #number of question marks
132       check_rc(rc)
133       #--------------------------------------------------------------------------
134       # parameter attributes are stored in instance variable @parmArray so that
135       # they are available when execute method is called.
136       #--------------------------------------------------------------------------
137       if @numParms > 0           # get parameter marker attributes
138         1.upto(@numParms) do |i| # parameter number starts from 1
139           rc, type, size, decimalDigits = SQLDescribeParam(@handle, i)
140           check_rc(rc)
141           @parmArray << Parameter.new(type, size, decimalDigits)
142         end
143       end
144       @prepared = true
145       self
146     end
147
148     def execute(*parms)
149       raise "The statement was not prepared" if @prepared == false
150
151       if parms.size == 1 and parms[0].class == Array
152         parms = parms[0]
153       end
154
155       if @numParms != parms.size
156         raise "Number of parameters supplied does not match with the SQL statement"
157       end
158
159       if @numParms > 0            #need to bind parameters
160         #--------------------------------------------------------------------
161         #calling bindParms may not be safe. Look comment below.
162         #--------------------------------------------------------------------
163         #bindParms(parms)
164
165         valueArray = []
166         1.upto(@numParms) do |i|  # parameter number starts from 1
167           type = @parmArray[i - 1].class
168           size = @parmArray[i - 1].size
169           decimalDigits = @parmArray[i - 1].decimalDigits
170
171           if parms[i - 1].class == String
172             valueArray << parms[i - 1]
173           else
174             valueArray << parms[i - 1].to_s
175           end
176
177           rc = SQLBindParameter(@handle, i, type, size, decimalDigits, valueArray[i - 1])
178           check_rc(rc)
179         end
180       end
181
182       check_rc(SQLExecute(@handle))
183
184       if @numParms != 0
185         check_rc(SQLFreeStmt(@handle, SQL_RESET_PARAMS)) # Reset parameters
186       end
187
188       self
189     end
190
191     #-------------------------------------------------------------------------------
192     # The last argument(value) to SQLBindParameter is a deferred argument, that is,
193     # it should be available when SQLExecute is called. Even though "value" is
194     # local to bindParms method, it seems that it is available when SQLExecute
195     # is called. I am not sure whether it would still work if garbage collection
196     # is done between bindParms call and SQLExecute call inside the execute method
197     # above.
198     #-------------------------------------------------------------------------------
199     def bindParms(parms)        # This is the real thing. It uses SQLBindParms
200       1.upto(@numParms) do |i|  # parameter number starts from 1
201         rc, dataType, parmSize, decimalDigits = SQLDescribeParam(@handle, i)
202         check_rc(rc)
203         if parms[i - 1].class == String
204           value = parms[i - 1]
205         else
206           value = parms[i - 1].to_s
207         end
208         rc = SQLBindParameter(@handle, i, dataType, parmSize, decimalDigits, value)
209         check_rc(rc)
210       end
211     end
212  
213     #------------------------------------------------------------------------------
214     # bind method does not use DB2's SQLBindParams, but replaces "?" in the
215     # SQL statement with the value before passing the SQL statement to DB2.
216     # It is not efficient and can handle only strings since it puts everything in
217     # quotes.
218     #------------------------------------------------------------------------------
219     def bind(sql, args)                #does not use SQLBindParams
220       arg_index = 0
221       result = ""
222       tokens(sql).each do |part|
223         case part
224         when '?'
225           result << "'" + (args[arg_index]) + "'"  #put it into quotes
226           arg_index += 1
227         when '??'
228           result << "?"
229         else
230           result << part
231         end
232       end
233       if arg_index < args.size
234         raise "Too many SQL parameters"
235       elsif arg_index > args.size
236         raise "Not enough SQL parameters"
237       end
238       result
239     end
240
241     ## Break the sql string into parts.
242     #
243     # This is NOT a full lexer for SQL.  It just breaks up the SQL
244     # string enough so that question marks, double question marks and
245     # quoted strings are separated.  This is used when binding
246     # arguments to "?" in the SQL string.  Note: comments are not
247     # handled.
248     #
249     def tokens(sql)
250       toks = sql.scan(/('([^'\\]|''|\\.)*'|"([^"\\]|""|\\.)*"|\?\??|[^'"?]+)/)
251       toks.collect { |t| t[0] }
252     end
253
254     def exec_direct(sql)
255       check_rc(SQLExecDirect(@handle, sql))
256       self
257     end
258
259     def set_cursor_name(name)
260       check_rc(SQLSetCursorName(@handle, name))
261       self
262     end
263
264     def get_cursor_name
265       rc, name = SQLGetCursorName(@handle)
266       check_rc(rc)
267       name
268     end
269
270     def row_count
271       rc, rowcount = SQLRowCount(@handle)
272       check_rc(rc)
273       rowcount
274     end
275
276     def num_result_cols
277       rc, cols = SQLNumResultCols(@handle)
278       check_rc(rc)
279       cols
280     end
281
282     def fetch_all
283       if block_given?
284         while row = fetch do
285           yield row
286         end
287       else
288         res = []
289         while row = fetch do
290           res << row
291         end
292         res
293       end
294     end
295
296     def fetch
297       cols = get_col_desc
298       rc = SQLFetch(@handle)
299       if rc == SQL_NO_DATA_FOUND
300         SQLFreeStmt(@handle, SQL_CLOSE)        # Close cursor
301         SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
302         return nil
303       end
304       raise "ERROR" unless rc == SQL_SUCCESS
305
306       retval = []
307       cols.each_with_index do |c, i|
308         rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1) #yun added 1 to c[2]
309         retval << adjust_content(content)
310       end
311       retval
312     end
313
314     def fetch_as_hash
315       cols = get_col_desc
316       rc = SQLFetch(@handle)
317       if rc == SQL_NO_DATA_FOUND
318         SQLFreeStmt(@handle, SQL_CLOSE)        # Close cursor
319         SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
320         return nil
321       end
322       raise "ERROR" unless rc == SQL_SUCCESS
323
324       retval = {}
325       cols.each_with_index do |c, i|
326         rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1)   #yun added 1 to c[2]
327         retval[c[0]] = adjust_content(content)
328       end
329       retval
330     end
331
332     def get_col_desc
333       rc, nr_cols = SQLNumResultCols(@handle)
334       cols = (1..nr_cols).collect do |c|
335         rc, name, bl, type, col_sz = SQLDescribeCol(@handle, c, 1024)
336         [name.downcase, type, col_sz]
337       end
338     end
339
340     def adjust_content(c)
341       case c.class.to_s
342       when 'DB2CLI::NullClass'
343         return nil
344       when 'DB2CLI::Time'
345         "%02d:%02d:%02d" % [c.hour, c.minute, c.second]
346       when 'DB2CLI::Date'
347         "%04d-%02d-%02d" % [c.year, c.month, c.day]
348       when 'DB2CLI::Timestamp'
349         "%04d-%02d-%02d %02d:%02d:%02d" % [c.year, c.month, c.day, c.hour, c.minute, c.second]
350       else
351         return c
352       end
353     end
354   end
355
356   class Parameter
357     attr_reader :type, :size, :decimalDigits
358     def initialize(type, size, decimalDigits)
359       @type, @size, @decimalDigits = type, size, decimalDigits
360     end
361   end
362 end
Note: See TracBrowser for help on using the browser.