1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import sql_base
24 import sql_types
25 import cx_Oracle
26 import sys
27 import string
28 import os
29 import re
30 from spacewalk.common import usix
31
32 from rhn.UserDictCase import UserDictCase
33 from spacewalk.common.usix import raise_with_tb, next
34 from spacewalk.server import rhnSQL
35 from spacewalk.common import rhnConfig
36 from spacewalk.common.rhnLog import log_debug, log_error
37 from spacewalk.common.rhnException import rhnException
38 from spacewalk.common.stringutils import to_string
39 from const import ORACLE
40
41 ORACLE_TYPE_MAPPING = [
42 (sql_types.NUMBER, cx_Oracle.NUMBER),
43 (sql_types.STRING, cx_Oracle.STRING),
44 (sql_types.BINARY, cx_Oracle.BINARY),
45 (sql_types.LONG_BINARY, cx_Oracle.LONG_BINARY),
46 ]
47
48
50
51 """
52 Wrapper that should just transform Oracle specific exceptions into
53 something generic from sql_base.
54 """
55 OracleError = cx_Oracle.DatabaseError
56
57 - def __init__(self, dbh, sql=None, force=None, blob_map=None):
58
59 try:
60 sql_base.Cursor.__init__(self, dbh=dbh, sql=sql,
61 force=force)
62 self._type_mapping = ORACLE_TYPE_MAPPING
63 self.blob_map = blob_map
64 except sql_base.SQLSchemaError:
65 e = sys.exc_info()[1]
66 (errno, errmsg) = e.errno, e.errmsg
67 if 900 <= errno <= 999:
68
69 raise_with_tb(sql_base.SQLStatementPrepareError(self.dbh, errmsg, self.sql), sys.exc_info()[2])
70
71
72
73
74
75
76
77
78 raise_with_tb(rhnException("Can not prepare statement", e.args), sys.exc_info()[2])
79
86
95
97 params = ','.join(["%s: %s" % (repr(key), repr(value)) for key, value
98 in list(kw.items())])
99 log_debug(5, "Executing SQL: \"%s\" with bind params: {%s}"
100 % (self.sql, params))
101 if self.sql is None:
102 raise rhnException("Cannot execute empty cursor")
103 if self.blob_map:
104 blob_content = {}
105 for orig_blob_var in list(self.blob_map.keys()):
106 new_blob_var = orig_blob_var + '_blob'
107 blob_content[new_blob_var] = kw[orig_blob_var]
108 kw[new_blob_var] = self.var(cx_Oracle.BLOB)
109 del kw[orig_blob_var]
110 modified_params = self._munge_args(kw)
111
112 try:
113 retval = function(*p, **kw)
114 except self.OracleError:
115 e = sys.exc_info()[1]
116 ret = self._get_oracle_error_info(e)
117 if isinstance(ret, usix.StringType):
118 raise_with_tb(sql_base.SQLError(self.sql, p, kw, ret), sys.exc_info()[2])
119 (errno, errmsg) = ret[:2]
120 if 900 <= errno <= 999:
121
122 raise_with_tb(sql_base.SQLStatementPrepareError(errno, errmsg, self.sql), sys.exc_info()[2])
123 if errno == 1475:
124 if self.reparsed:
125 log_error("Reparsing cursor did not fix it", self.sql)
126 args = ("Reparsing tried and still got this",) + tuple(ret)
127 raise_with_tb(sql_base.SQLError(*args), sys.exc_info()[2])
128 self._real_cursor = self.dbh.prepare(self.sql)
129 self.reparsed = 1
130 self._execute_wrapper(function, *p, **kw)
131 elif 20000 <= errno <= 20999:
132 raise_with_tb(sql_base.SQLSchemaError(*ret), sys.exc_info()[2])
133 raise_with_tb(sql_base.SQLError(*ret), sys.exc_info()[2])
134 except ValueError:
135
136 raise
137 else:
138 self.reparsed = 0
139
140 if self.blob_map:
141 for blob_var, content in blob_content.items():
142 kw[blob_var].getvalue().write(content)
143
144 self._unmunge_args(kw, modified_params)
145 return retval
146
148 """
149 Oracle specific execution of the query.
150 """
151
152
153
154 _p = UserDictCase(kwargs)
155 params = {}
156
157
158
159 for k in self._real_cursor.bindnames():
160 if not _p.has_key(k):
161
162 raise sql_base.SQLError(1008, 'Not all variables bound', k)
163 params[k] = to_string(_p[k])
164
165
166
167 try:
168 self._real_cursor.execute(*(None, ), **params)
169 except cx_Oracle.OperationalError:
170 e = sys.exc_info()[1]
171 raise sql_base.SQLError("Cannot execute SQL statement: %s" % str(e))
172
173 self.description = self._real_cursor.description
174 return self._real_cursor.rowcount
175
177
178 if not kwargs:
179 return 0
180
181 max_array_size = 25
182 i = iter(list(kwargs.values()))
183 firstval = next(i)
184 array_size = len(firstval)
185 if array_size == 0:
186 return 0
187
188 chunk_size = min(max_array_size, array_size)
189 pdict = {}
190 for k in kwargs.iterkeys():
191 pdict[k] = None
192 arr = []
193 for i in range(chunk_size):
194 arr.append(pdict.copy())
195
196
197 rowcount = 0
198 start = 0
199 while start < array_size:
200 item_count = min(array_size - start, chunk_size)
201
202 if item_count != chunk_size:
203 arr = arr[:item_count]
204
205 for i in range(item_count):
206 pdict = arr[i]
207 for k, v in kwargs.items():
208 pdict[k] = to_string(v[start + i])
209
210
211
212
213
214 self._real_cursor.setinputsizes(**{})
215
216
217
218
219 self._real_cursor.executemany(None, arr)
220 self.description = self._real_cursor.description
221
222 rowcount = rowcount + self._real_cursor.rowcount
223 start = start + chunk_size
224
225 return rowcount
226
228 if isinstance(error, cx_Oracle.DatabaseError):
229 e = error[0]
230 if isinstance(e, cx_Oracle._Error):
231 return (e.code, e.message, self.sql)
232 return (None, str(error), self.sql)
233
234
236 if hasattr(self._real_cursor, name):
237 return getattr(self._real_cursor, name)
238 raise AttributeError(name)
239
240
242 self.reparsed = 0
243 self.dbh = self.sql = self._real_cursor = None
244
250
252 for sqltype, dbtype in self._type_mapping:
253 if isinstance(val, sqltype):
254 var = self._real_cursor.var(dbtype, val.size)
255 var.setvalue(0, val.get_value())
256 return var
257
258
259
260 return val.get_value()
261
263 for k, v in modified_params:
264 v.set_value(kw_dict[k].getvalue())
265
266
276
277 - def update_blob(self, table_name, column_name, where_clause, data,
278 **kwargs):
286
287
289 OracleError = cx_Oracle.DatabaseError
290
294
296 """
297 Wrap the __call__ method from the parent class to catch Oracle specific
298 actions and convert them to something generic.
299 """
300 log_debug(2, self.name, args)
301 retval = None
302 try:
303 retval = self._call_proc(args)
304 except cx_Oracle.DatabaseError:
305 e = sys.exc_info()[1]
306 if not hasattr(e, "args"):
307 raise_with_tb(sql_base.SQLError(self.name, args), sys.exc_info()[2])
308 elif 20000 <= e[0].code <= 20999:
309
310 raise_with_tb(sql_base.SQLSchemaError(e[0].code, str(e[0])), sys.exc_info()[2])
311
312 raise_with_tb(sql_base.SQLError(e[0].code, str(e[0])), sys.exc_info()[2])
313 except cx_Oracle.NotSupportedError:
314 error = sys.exc_info()[1]
315 raise_with_tb(sql_base.SQLError(*error.args), sys.exc_info()[2])
316 return retval
317
319 """
320 Converts database specific argument types to those defined in sql_base.
321 """
322 new_args = []
323 for arg in args:
324 if not isinstance(arg, sql_types.DatabaseDataType):
325 new_args.append(arg)
326 continue
327 new_args.append(self._munge_arg(arg))
328 return new_args
329
331 for sqltype, db_specific_type in self._type_mapping:
332 if isinstance(val, sqltype):
333 var = self.cursor.var(db_specific_type, val.size)
334 var.setvalue(0, val.get_value())
335 return var
336
337
338 return val.get_value()
339
342
344 args = list(map(to_string, self._munge_args(args)))
345 if ret_type:
346 ret_type_mapped = False
347 for sqltype, db_type in self._type_mapping:
348 if isinstance(ret_type, sqltype):
349 ret_type = db_type
350 ret_type_mapped = True
351 break
352 if not ret_type_mapped:
353 raise Exception("Unknown type", ret_type)
354
355 if ret_type:
356 return self.cursor.callfunc(self.name, ret_type, args)
357 else:
358 return self.cursor.callproc(self.name, args)
359
360
362
363 - def __init__(self, name, proc, ret_type):
366
369
370
372 _cursor_class = Cursor
373 _procedure_class = Procedure
374 TimestampFromTicks = cx_Oracle.TimestampFromTicks
375 OracleError = cx_Oracle.DatabaseError
376
377 - def __init__(self, host=None, port=None, username=None,
378 password=None, database=None, sslmode=None, sslrootcert=None):
379
380
381 if not (username and password and database):
382 raise AttributeError("A valid Oracle username, password, and SID are required.")
383 if sslmode is not None:
384 raise AttributeError("Option sslmode is not supported for Oracle database backend.")
385 if sslrootcert is not None:
386 raise AttributeError("Option sslrootcert is not supported for Oracle database backend.")
387
388 sql_base.Database.__init__(self)
389
390 self.username = username
391 self.password = password
392 self.database = database
393
394
395 self.dbtxt = self.username + '@' + self.database
396
397 self.dbh = None
398
399
400
401 self.stderr = sys.stderr
402
404 log_debug(1, "Connecting to database", self.dbtxt)
405 self._fix_environment_vars()
406 try:
407 self.dbh = self._connect()
408 except self.OracleError:
409 e = sys.exc_info()[1]
410 ret = self._get_oracle_error_info(e)
411 if isinstance(ret, usix.StringType):
412 raise_with_tb(sql_base.SQLConnectError(self.dbtxt, -1,
413 "Unable to connect to database", ret), sys.exc_info()[2])
414 (errno, errmsg) = ret[:2]
415 log_error("Connection attempt failed", errno, errmsg)
416 if reconnect:
417
418
419
420 if errno in [12547]:
421 return self.connect(reconnect=0)
422 err_args = [self.dbtxt, errno, errmsg]
423 err_args.extend(list(ret[2:]))
424 raise_with_tb(sql_base.SQLConnectError(*err_args), sys.exc_info()[2])
425
426 raise_with_tb(sql_base.SQLConnectError(*(
427 [self.dbtxt, errno, errmsg,
428 "Attempting Re-Connect to the database failed", ] + ret[2:])), sys.exc_info()[2])
429 dbh_id = id(self.dbh)
430
431 self._cursor_class._cursor_cache[dbh_id] = {}
432
434 dbh = cx_Oracle.Connection(self.username, self.password, self.database)
435 if hasattr(sys, "argv"):
436 dbh.cursor().execute(
437 "BEGIN DBMS_APPLICATION_INFO.SET_MODULE('%s',NULL); END;"
438 % sys.argv[0])
439 return dbh
440
441 - def is_connected_to(self, backend, host, port, username, password,
442 database, sslmode, sslrootcert):
447
448
468
470 return self._cursor_class(dbh=self.dbh)
471
472
473 - def prepare(self, sql, force=0, blob_map=None):
474
475 sql = string.join([a for a in list(map(string.strip,
476 [(a + " ")[:string.find(a, '--')] for a in string.split(sql, "\n")])) if len(a)],
477 " ")
478 if blob_map:
479 col_list = []
480 bind_list = []
481 for bind_var, column in blob_map.items():
482 r = re.compile(":%s" % bind_var)
483 sql = re.sub(r, 'empty_blob()', sql)
484 col_list.append(column)
485 bind_list.append(":%s_blob" % bind_var)
486 sql += " returning %s into %s" % (','.join(col_list), ','.join(bind_list))
487
488 return self._cursor_class(dbh=self.dbh, sql=sql, force=force, blob_map=blob_map)
489
499
508
509
510 - def execute(self, sql, *args, **kwargs):
514
515
520
522 log_debug(3, self.dbtxt)
523 if self.dbh is not None:
524 return self.dbh.commit()
525
531
533 try:
534 h = self.prepare("select 1 from dual")
535 h.execute()
536 except:
537 log_error("DATABASE CONNECTION TO '%s' LOST" % self.dbtxt,
538 "Exception information: %s" % sys.exc_info()[1])
539 self.connect()
540 return 0
541
542
559
560
561
567
569 if not lob:
570 return None
571 return lob.read()
572
573 - def Date(self, year, month, day):
574 return cx_Oracle.Date(year, month, day)
575
578