from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.gateway import ObjSpace, W_Root, NoneNotWrapped from pypy.interpreter.argument import Arguments from pypy.interpreter.typedef import TypeDef, GetSetProperty from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w from pypy.interpreter.gateway import interp2app from pypy.interpreter.error import OperationError from pypy.rpython.lltypesystem import rffi, lltype from pypy.module.oracle import roci, interp_error from pypy.module.oracle.config import w_string, string_w, StringBuffer from pypy.module.oracle import interp_variable from pypy.module.oracle.interp_error import get # XXX are those "assert isinstance(xxx, interp_variable.W_Variable)" necessary? # the bindList should annotate to SomeList(SomeInstance(W_Variable)) class W_Cursor(Wrappable): def __init__(self, space, connection): self.connection = connection self.environment = connection.environment self.w_statement = None self.statementType = -1 self.handle = lltype.nullptr(roci.OCIStmt.TO) self.isOpen = True self.isOwned = False self.setInputSizes = False self.arraySize = 50 self.fetchArraySize = 50 self.bindArraySize = 1 self.bindList = None self.bindDict = None self.numbersAsStrings = False self.outputSize = -1 self.outputSizeColumn = -1 self.w_inputTypeHandler = None self.w_outputTypeHandler = None self.w_rowFactory = None def execute(self, space, w_stmt, __args__): args_w, kw_w = __args__.unpack() if space.is_w(w_stmt, space.w_None): w_stmt = None if len(args_w) > 1: raise OperationError( space.w_TypeError, space.wrap("Too many arguments")) elif len(args_w) == 1: if len(kw_w) > 0: raise OperationError( interp_error.get(space).w_InterfaceError, space.wrap( "expecting argument or keyword arguments, not both")) w_vars = args_w[0] elif len(kw_w) > 0: w_vars = space.newdict() for key, w_value in kw_w.iteritems(): space.setitem(w_vars, space.wrap(key), w_value) else: w_vars = None # make sure the cursor is open self._checkOpen(space) return self._execute(space, w_stmt, w_vars) execute.unwrap_spec = ['self', ObjSpace, W_Root, Arguments] def prepare(self, space, w_stmt, w_tag=None): # make sure the cursor is open self._checkOpen(space) # prepare the statement self._internalPrepare(space, w_stmt, w_tag) prepare.unwrap_spec = ['self', ObjSpace, W_Root, W_Root] def _execute(self, space, w_stmt, w_vars): # prepare the statement, if applicable self._internalPrepare(space, w_stmt, None) # perform binds if w_vars is None: pass elif space.is_true(space.isinstance(w_vars, space.w_dict)): self._setBindVariablesByName(space, w_vars, 1, 0, 0) else: self._setBindVariablesByPos(space, w_vars, 1, 0, 0) self._performBind(space) # execute the statement isQuery = self.statementType == roci.OCI_STMT_SELECT if isQuery: numIters = 0 else: numIters = 1 self._internalExecute(space, numIters=numIters) # perform defines, if necessary if isQuery and self.fetchVariables is None: self._performDefine() # reset the values of setoutputsize() self.outputSize = -1 self.outputSizeColumn = -1 # for queries, return the cursor for convenience if isQuery: return space.wrap(self) # for all other statements, simply return None return space.w_None def executemany(self, space, w_stmt, w_list_of_args): if space.is_w(w_stmt, space.w_None): w_stmt = None if not space.is_true(space.isinstance(w_list_of_args, space.w_list)): raise OperationError( space.w_TypeError, space.wrap("list expected")) # make sure the cursor is open self._checkOpen(space) # prepare the statement self._internalPrepare(space, w_stmt, None) # queries are not supported as the result is undefined if self.statementType == roci.OCI_STMT_SELECT: raise OperationError( get(space).w_NotSupportedError, space.wrap("queries not supported: results undefined")) # perform binds args_w = space.listview(w_list_of_args) numrows = len(args_w) for i in range(numrows): w_arguments = args_w[i] deferred = i < numrows - 1 if space.is_true(space.isinstance(w_arguments, space.w_dict)): self._setBindVariablesByName( space, w_arguments, numrows, i, deferred) else: self._setBindVariablesByPos( space, w_arguments, numrows, i, deferred) self._performBind(space) # execute the statement, but only if the number of rows is greater than # zero since Oracle raises an error otherwise if numrows > 0: self._internalExecute(space, numIters=numrows) executemany.unwrap_spec = ['self', ObjSpace, W_Root, W_Root] def close(self, space): # make sure we are actually open self._checkOpen(space) # close the cursor self.freeHandle(space, raiseError=True) self.isOpen = False self.handle = lltype.nullptr(roci.OCIStmt.TO) close.unwrap_spec = ['self', ObjSpace] def callfunc(self, space, name, w_returnType, w_parameters=None): retvar = interp_variable.newVariableByType(space, self, w_returnType, 1) if space.is_w(w_parameters, space.w_None): w_parameters = None self._call(space, name, retvar, w_parameters) # determine the results return retvar.getValue(space, 0) callfunc.unwrap_spec = ['self', ObjSpace, str, W_Root, W_Root] def callproc(self, space, name, w_parameters=None): if space.is_w(w_parameters, space.w_None): w_parameters = None self._call(space, name, None, w_parameters) # create the return value ret_w = [] if self.bindList: for v in self.bindList: assert isinstance(v, interp_variable.W_Variable) ret_w.append(v.getValue(space, 0)) return space.newlist(ret_w) callproc.unwrap_spec = ['self', ObjSpace, str, W_Root] def _call(self, space, name, retvar, w_args): # determine the number of arguments passed if w_args: numArguments = space.int_w(space.len(w_args)) else: numArguments = 0 # make sure we are actually open self._checkOpen(space) # add the return value, if applicable if retvar: offset = 1 w_vars = space.newlist([retvar]) if w_args: space.call_method(w_vars, "extend", w_args) else: offset = 0 w_vars = w_args # build up the statement args = ', '.join([':%d' % (i + offset + 1,) for i in range(numArguments)]) if retvar: stmt = "begin :1 := %s(%s); end;" % (name, args) else: stmt = "begin %s(%s); end;" % (name, args) self._execute(space, space.wrap(stmt), w_vars) def _checkOpen(self, space): if not self.isOpen: raise OperationError( interp_error.get(space).w_InterfaceError, space.wrap("not open")) def allocateHandle(self): handleptr = lltype.malloc(rffi.CArrayPtr(roci.OCIStmt).TO, 1, flavor='raw') try: status = roci.OCIHandleAlloc( self.environment.handle, handleptr, roci.OCI_HTYPE_STMT, 0, lltype.nullptr(rffi.CArray(roci.dvoidp))) self.environment.checkForError( status, "Cursor_New()") self.handle = handleptr[0] finally: lltype.free(handleptr, flavor='raw') self.isOwned = True def freeHandle(self, space, raiseError=True): if not self.handle: return if self.isOwned: roci.OCIHandleFree(self.handle, roci.OCI_HTYPE_STMT) elif self.connection.handle: tagBuffer = StringBuffer() tagBuffer.fill(space, self.w_statementTag) try: status = roci.OCIStmtRelease( self.handle, self.environment.errorHandle, tagBuffer.ptr, tagBuffer.size, roci.OCI_DEFAULT) self.environment.checkForError( status, "Cursor_FreeHandle()") finally: tagBuffer.clear() def _internalPrepare(self, space, w_stmt, w_tag): # make sure we don't get a situation where nothing is to be executed if w_stmt is None and self.w_statement is None: raise OperationError( interp_error.get(space).w_ProgrammingError, space.wrap("no statement specified " "and no prior statement prepared")) # nothing to do if the statement is identical to the one already stored # but go ahead and prepare anyway for create, alter and drop statments if w_stmt is None or w_stmt == self.w_statement: if self.statementType not in (roci.OCI_STMT_CREATE, roci.OCI_STMT_DROP, roci.OCI_STMT_ALTER): return w_stmt = self.w_statement else: self.w_statement = w_stmt # release existing statement, if necessary self.w_statementTag = w_tag self.freeHandle(space) # prepare statement self.isOwned = False handleptr = lltype.malloc(roci.Ptr(roci.OCIStmt).TO, 1, flavor='raw') stmtBuffer = StringBuffer() tagBuffer = StringBuffer() stmtBuffer.fill(space, w_stmt) tagBuffer.fill(space, w_tag) try: status = roci.OCIStmtPrepare2( self.connection.handle, handleptr, self.environment.errorHandle, stmtBuffer.ptr, stmtBuffer.size, tagBuffer.ptr, tagBuffer.size, roci.OCI_NTV_SYNTAX, roci.OCI_DEFAULT) self.environment.checkForError( status, "Connection_InternalPrepare(): prepare") self.handle = handleptr[0] finally: lltype.free(handleptr, flavor='raw') stmtBuffer.clear() tagBuffer.clear() # clear bind variables, if applicable if not self.setInputSizes: self.bindList = None self.bindDict = None # clear row factory, if applicable self.rowFactory = None # determine if statement is a query self._getStatementType() def _setErrorOffset(self, space, e): if e.match(space, get(space).w_DatabaseError): attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub4).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_PARSE_ERROR_OFFSET, self.environment.errorHandle) e.offset = attrptr[0] finally: lltype.free(attrptr, flavor='raw') def _internalExecute(self, space, numIters): if self.connection.autocommit: mode = roci.OCI_COMMIT_ON_SUCCESS else: mode = roci.OCI_DEFAULT status = roci.OCIStmtExecute( self.connection.handle, self.handle, self.environment.errorHandle, numIters, 0, lltype.nullptr(roci.OCISnapshot.TO), lltype.nullptr(roci.OCISnapshot.TO), mode) try: self.environment.checkForError( status, "Cursor_InternalExecute()") except OperationError, e: self._setErrorOffset(space, e) raise finally: self._setRowCount() def _getStatementType(self): attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub2).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_STMT_TYPE, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_GetStatementType()") self.statementType = rffi.cast(lltype.Signed, attrptr[0]) finally: lltype.free(attrptr, flavor='raw') self.fetchVariables = None def getDescription(space, self): "Return a list of 7-tuples consisting of the description of " "the define variables" # make sure the cursor is open self._checkOpen(space) # fixup bound cursor, if necessary self._fixupBoundCursor() # if not a query, return None if self.statementType != roci.OCI_STMT_SELECT: return # determine number of items in select-list attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub1).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_PARAM_COUNT, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_GetDescription()") numItems = attrptr[0] finally: lltype.free(attrptr, flavor='raw') return space.newlist( [space.newtuple(self._itemDescription(space, i + 1)) for i in range(numItems)]) def _itemDescription(self, space, pos): "Return a tuple describing the item at the given position" # acquire parameter descriptor paramptr = lltype.malloc(roci.Ptr(roci.OCIParam).TO, 1, flavor='raw') try: status = roci.OCIParamGet( self.handle, roci.OCI_HTYPE_STMT, self.environment.errorHandle, rffi.cast(roci.dvoidpp, paramptr), pos) self.environment.checkForError( status, "Cursor_GetDescription(): parameter") param = paramptr[0] finally: lltype.free(paramptr, flavor='raw') try: # acquire usable type of item varType = interp_variable.typeByOracleDescriptor( param, self.environment) # acquire internal size of item attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub2).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( param, roci.OCI_HTYPE_DESCRIBE, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_DATA_SIZE, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_ItemDescription(): internal size") internalSize = rffi.cast(lltype.Signed, attrptr[0]) finally: lltype.free(attrptr, flavor='raw') # acquire name of item nameptr = lltype.malloc(rffi.CArrayPtr(roci.oratext).TO, 1, flavor='raw') lenptr = lltype.malloc(rffi.CArrayPtr(roci.ub4).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( param, roci.OCI_HTYPE_DESCRIBE, rffi.cast(roci.dvoidp, nameptr), lenptr, roci.OCI_ATTR_NAME, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_ItemDescription(): name") name = rffi.charpsize2str(nameptr[0], lenptr[0]) finally: lltype.free(nameptr, flavor='raw') lltype.free(lenptr, flavor='raw') # lookup precision and scale if varType is interp_variable.VT_Float: attrptr = lltype.malloc(rffi.CArrayPtr(roci.sb1).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( param, roci.OCI_HTYPE_DESCRIBE, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_SCALE, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_ItemDescription(): scale") scale = rffi.cast(lltype.Signed, attrptr[0]) finally: lltype.free(attrptr, flavor='raw') attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub2).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( param, roci.OCI_HTYPE_DESCRIBE, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_PRECISION, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_ItemDescription(): precision") precision = rffi.cast(lltype.Signed, attrptr[0]) finally: lltype.free(attrptr, flavor='raw') else: scale = 0 precision = 0 # lookup whether null is permitted for the attribute attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub1).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( param, roci.OCI_HTYPE_DESCRIBE, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_IS_NULL, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_ItemDescription(): nullable") nullable = rffi.cast(lltype.Signed, attrptr[0]) != 0 finally: lltype.free(attrptr, flavor='raw') # set display size based on data type if varType is interp_variable.VT_String: displaySize = internalSize elif varType is interp_variable.VT_NationalCharString: displaySize = internalSize / 2 elif varType is interp_variable.VT_Binary: displaySize = internalSize elif varType is interp_variable.VT_FixedChar: displaySize = internalSize elif varType is interp_variable.VT_FixedNationalChar: displaySize = internalSize / 2 elif varType is interp_variable.VT_Float: if precision: displaySize = precision + 1 if scale > 0: displaySize += scale + 1 else: displaySize = 127 elif varType is interp_variable.VT_DateTime: displaySize = 23 else: displaySize = -1 # return the tuple return [space.wrap(name), space.gettypeobject(varType.typedef), space.wrap(displaySize), space.wrap(internalSize), space.wrap(precision), space.wrap(scale), space.wrap(nullable)] finally: roci.OCIDescriptorFree(param, roci.OCI_DTYPE_PARAM) def _setBindVariablesByPos(self, space, w_vars, numElements, arrayPos, defer): "handle positional binds" # make sure positional and named binds are not being intermixed if self.bindDict is not None: raise OperationError( get(space).w_ProgrammingError, space.wrap("positional and named binds cannot be intermixed")) if self.bindList is None: self.bindList = [] vars_w = space.fixedview(w_vars) for i in range(len(vars_w)): w_value = vars_w[i] if i < len(self.bindList): origVar = self.bindList[i] if space.is_w(origVar, space.w_None): origVar = None else: origVar = None newVar = self._setBindVariableHelper(space, w_value, origVar, numElements, arrayPos, defer) if newVar: if i < len(self.bindList): self.bindList[i] = newVar else: assert i == len(self.bindList) self.bindList.append(newVar) def _setBindVariablesByName(self, space, w_vars, numElements, arrayPos, defer): "handle named binds" # make sure positional and named binds are not being intermixed if self.bindList is not None: raise OperationError( get(space).w_ProgrammingError, space.wrap("positional and named binds cannot be intermixed")) if self.bindDict is None: self.bindDict = space.newdict() items = space.fixedview(space.call_method(w_vars, "iteritems")) for item in items: w_key, w_value = space.fixedview(item, 2) origVar = space.finditem(self.bindDict, w_key) newVar = self._setBindVariableHelper(space, w_value, origVar, numElements, arrayPos, defer) if newVar: space.setitem(self.bindDict, w_key, newVar) def _setBindVariableHelper(self, space, w_value, origVar, numElements, arrayPos, defer): valueIsVariable = space.is_true(space.isinstance(w_value, get(space).w_Variable)) newVar = None # handle case where variable is already bound if origVar: assert isinstance(origVar, interp_variable.W_Variable) # if the value is a variable object, rebind it if necessary if valueIsVariable: newVar = space.interp_w(interp_variable.W_Variable, w_value) assert isinstance(newVar, interp_variable.W_Variable) if newVar == origVar: newVar = None # if the number of elements has changed, create a new variable # this is only necessary for executemany() since execute() always # passes a value of 1 for the number of elements elif numElements > origVar.allocatedElements: newVar = origVar.clone( self, numElements, origVar.size) assert isinstance(newVar, interp_variable.W_Variable) newVar.setValue(space, arrayPos, w_value) # otherwise, attempt to set the value else: try: origVar.setValue(space, arrayPos, w_value) except OperationError, e: # executemany() should simply fail after the first element if arrayPos > 0: raise # anything other than IndexError or TypeError should fail if (not e.match(space, space.w_IndexError) and not e.match(space, space.w_TypeError)): raise # catch the exception and try to create a new variable origVar = None if not origVar: # if the value is a variable object, bind it directly if valueIsVariable: newVar = space.interp_w(interp_variable.W_Variable, w_value) assert isinstance(newVar, interp_variable.W_Variable) newVar.boundPos = 0 newVar.boundName = None # otherwise, create a new variable, unless the value is None and # we wish to defer type assignment elif not space.is_w(w_value, space.w_None) or not defer: newVar = interp_variable.newVariableByValue(space, self, w_value, numElements) assert isinstance(newVar, interp_variable.W_Variable) newVar.setValue(space, arrayPos, w_value) assert newVar is None or isinstance(newVar, interp_variable.W_Variable) return newVar def _performBind(self, space): # set values and perform binds for all bind variables if self.bindList: for i in range(len(self.bindList)): var = self.bindList[i] assert isinstance(var, interp_variable.W_Variable) var.bind(space, self, None, i + 1) if self.bindDict: items_w = space.fixedview( space.call_method(self.bindDict, "iteritems")) for w_item in items_w: w_key, var = space.fixedview(w_item, 2) assert isinstance(var, interp_variable.W_Variable) var.bind(space, self, w_key, 0) # ensure that input sizes are reset self.setInputSizes = False def _setRowCount(self): if self.statementType == roci.OCI_STMT_SELECT: self.rowCount = 0 self.actualRows = -1 self.rowNum = 0 elif self.statementType in (roci.OCI_STMT_INSERT, roci.OCI_STMT_UPDATE, roci.OCI_STMT_DELETE): attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub4).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_ROW_COUNT, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_SetRowCount()") self.rowCount = rffi.cast(lltype.Signed, attrptr[0]) finally: lltype.free(attrptr, flavor='raw') else: self.rowCount = -1 def _performDefine(self): # determine number of items in select-list attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub4).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_PARAM_COUNT, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_PerformDefine()") numParams = attrptr[0] finally: lltype.free(attrptr, flavor='raw') self.fetchVariables = [] # define a variable for each select-item self.fetchArraySize = self.arraySize for i in range(numParams): var = interp_variable.define(self, i+1, self.fetchArraySize) assert isinstance(var, interp_variable.W_Variable) self.fetchVariables.append(var) def _verifyFetch(self, space): # make sure the cursor is open self._checkOpen(space) # fixup bound cursor, if necessary self._fixupBoundCursor() # make sure the cursor is for a query if self.statementType != roci.OCI_STMT_SELECT: raise OperationError( get(space).w_InterfaceError, space.wrap("not a query")) def _fixupBoundCursor(self): if self.handle and self.statementType < 0: self._getStatementType() if self.statementType == roci.OCI_STMT_SELECT: self._performDefine() self._setRowCount() def fetchone(self, space): # verify fetch can be performed self._verifyFetch(space) # setup return value if self._moreRows(space): return self._createRow(space) return space.w_None fetchone.unwrap_spec = ['self', ObjSpace] def fetchmany(self, space, w_numRows=NoneNotWrapped): if w_numRows is not None: numRows = space.int_w(w_numRows) else: numRows = self.arraySize # verify fetch can be performed self._verifyFetch(space) return self._multiFetch(space, limit=numRows) fetchmany.unwrap_spec = ['self', ObjSpace, W_Root] def fetchall(self, space): # verify fetch can be performed self._verifyFetch(space) return self._multiFetch(space, limit=0) fetchall.unwrap_spec = ['self', ObjSpace] def descr_iter(self, space): self._verifyFetch(space) return space.wrap(self) descr_iter.unwrap_spec = ['self', ObjSpace] def descr_next(self, space): # verify fetch can be performed self._verifyFetch(space) # setup return value if self._moreRows(space): return self._createRow(space) raise OperationError(space.w_StopIteration, space.w_None) descr_next.unwrap_spec = ['self', ObjSpace] def _moreRows(self, space): if self.rowNum < self.actualRows: return True if self.actualRows < 0 or self.actualRows == self.fetchArraySize: self._internalFetch(space, self.fetchArraySize) if self.rowNum < self.actualRows: return True return False def _internalFetch(self, space, numRows): if not self.fetchVariables: raise OperationError( get(space).w_InterfaceError, space.wrap("query not executed")) status = roci.OCIStmtFetch( self.handle, self.environment.errorHandle, numRows, roci.OCI_FETCH_NEXT, roci.OCI_DEFAULT) if status != roci.OCI_NO_DATA: self.environment.checkForError( status, "Cursor_InternalFetch(): fetch") for var in self.fetchVariables: assert isinstance(var, interp_variable.W_Variable) var.internalFetchNum += 1 attrptr = lltype.malloc(rffi.CArrayPtr(roci.ub4).TO, 1, flavor='raw') try: status = roci.OCIAttrGet( self.handle, roci.OCI_HTYPE_STMT, rffi.cast(roci.dvoidp, attrptr), lltype.nullptr(roci.Ptr(roci.ub4).TO), roci.OCI_ATTR_ROW_COUNT, self.environment.errorHandle) self.environment.checkForError( status, "Cursor_InternalFetch(): row count") self.actualRows = (rffi.cast(lltype.Signed, attrptr[0]) - self.rowCount) self.rowNum = 0 finally: lltype.free(attrptr, flavor='raw') def _multiFetch(self, space, limit=0): results_w = [] rowNum = 0 # fetch as many rows as possible while limit == 0 or rowNum < limit: rowNum += 1 if not self._moreRows(space): break w_row = self._createRow(space) results_w.append(w_row) return space.newlist(results_w) def _createRow(self, space): items_w = [] numItems = len(self.fetchVariables) # acquire the value for each item for var in self.fetchVariables: assert isinstance(var, interp_variable.W_Variable) w_item = var.getValue(space, self.rowNum) items_w.append(w_item) # increment row counters self.rowNum += 1 self.rowCount += 1 w_row = space.newtuple(items_w) # if a row factory is defined, call it if self.w_rowFactory: w_row = space.call(self.w_rowFactory, w_row) return w_row def _get_bind_info(self, space, numElements): # avoid bus errors on 64bit platforms numElements = numElements + (rffi.sizeof(roci.dvoidp) - numElements % rffi.sizeof(roci.dvoidp)) # initialize the buffers bindNames = lltype.malloc(roci.Ptr(roci.oratext).TO, numElements, flavor='raw') bindNameLengths = lltype.malloc(roci.Ptr(roci.ub1).TO, numElements, flavor='raw') indicatorNames = lltype.malloc(roci.Ptr(roci.oratext).TO, numElements, flavor='raw') indicatorNameLengths = lltype.malloc(roci.Ptr(roci.ub1).TO, numElements, flavor='raw') duplicate = lltype.malloc(roci.Ptr(roci.ub1).TO, numElements, flavor='raw') bindHandles = lltype.malloc(roci.Ptr(roci.OCIBind).TO, numElements, flavor='raw') foundElementsPtr = lltype.malloc(roci.Ptr(roci.sb4).TO, 1, flavor='raw') try: status = roci.OCIStmtGetBindInfo( self.handle, self.environment.errorHandle, numElements, 1, foundElementsPtr, bindNames, bindNameLengths, indicatorNames, indicatorNameLengths, duplicate, bindHandles) if status != roci.OCI_NO_DATA: self.environment.checkForError( status, "Cursor_GetBindNames()") # Too few elements allocated foundElements = rffi.cast(lltype.Signed, foundElementsPtr[0]) if foundElements < 0: return -foundElements, None names_w = [] # process the bind information returned for i in range(foundElements): if rffi.cast(lltype.Signed, duplicate[i]): continue names_w.append( w_string(space, bindNames[i], rffi.cast(lltype.Signed, bindNameLengths[i]))) return 0, names_w finally: lltype.free(bindNames, flavor='raw') lltype.free(bindNameLengths, flavor='raw') lltype.free(indicatorNames, flavor='raw') lltype.free(indicatorNameLengths, flavor='raw') lltype.free(duplicate, flavor='raw') lltype.free(bindHandles, flavor='raw') lltype.free(foundElementsPtr, flavor='raw') def bindnames(self, space): # make sure the cursor is open self._checkOpen(space) # ensure that a statement has already been prepared if not self.w_statement: raise OperationError(get(space).w_ProgrammingError, space.wrap("statement must be prepared first")) nbElements, names = self._get_bind_info(space, 8) if nbElements: _, names = self._get_bind_info(space, nbElements) return space.newlist(names) bindnames.unwrap_spec = ['self', ObjSpace] def var(self, space, w_type, size=0, w_arraysize=None, w_inconverter=None, w_outconverter=None): if space.is_w(w_arraysize, space.w_None): arraySize = self.bindArraySize else: arraySize = space.int_w(w_arraysize) # determine the type of variable varType = interp_variable.typeByPythonType(space, self, w_type) if varType.isVariableLength and size == 0: size = varType.size # create the variable var = varType(self, arraySize, size) var.w_inconverter = w_inconverter var.w_outconverter = w_outconverter return space.wrap(var) var.unwrap_spec = ['self', ObjSpace, W_Root, int, W_Root, W_Root, W_Root] def arrayvar(self, space, w_type, w_value, size=0): # determine the type of variable varType = interp_variable.typeByPythonType(space, self, w_type) if varType.isVariableLength and size == 0: size = varType.size # determine the number of elements to create if space.is_true(space.isinstance(w_value, space.w_list)): numElements = space.int_w(space.len(w_value)) elif space.is_true(space.isinstance(w_value, space.w_int)): numElements = space.int_w(w_value) else: raise OperationError( get(space).w_NotSupportedError, space.wrap("expecting integer or list of values")) # create the variable var = varType(self, numElements, size) var.makeArray(space) # set the value, if applicable if space.is_true(space.isinstance(w_value, space.w_list)): var.setArrayValue(space, w_value) return var arrayvar.unwrap_spec = ['self', ObjSpace, W_Root, W_Root, int] def setinputsizes(self, space, __args__): args_w, kw_w = __args__.unpack() # only expect keyword arguments or positional arguments, not both if args_w and kw_w: raise OperationError( interp_error.get(space).w_InterfaceError, space.wrap( "expecting argument or keyword arguments, not both")) # make sure the cursor is open self._checkOpen(space) # eliminate existing bind variables self.bindList = None self.bindDict = None self.setInputSizes = True # process each input if kw_w: self.bindDict = space.newdict() for key, w_value in kw_w.iteritems(): var = interp_variable.newVariableByType( space, self, w_value, self.bindArraySize) space.setitem(self.bindDict, space.wrap(key), var) return self.bindDict else: self.bindList = [None] * len(args_w) for i in range(len(args_w)): w_value = args_w[i] if space.is_w(w_value, space.w_None): var = None else: var = interp_variable.newVariableByType( space, self, w_value, self.bindArraySize) self.bindList[i] = var return space.newlist(self.bindList) setinputsizes.unwrap_spec = ['self', ObjSpace, Arguments] def setoutputsize(self, space, outputSize, outputSizeColumn=-1): self.outputSize = outputSize self.outputSizeColumn = outputSizeColumn setoutputsize.unwrap_spec = ['self', ObjSpace, int, int] def arraysize_get(space, self): return space.wrap(self.arraySize) def arraysize_set(space, self, w_value): self.arraySize = space.int_w(w_value) def bindarraysize_get(space, self): return space.wrap(self.bindArraySize) def bindarraysize_set(space, self, w_value): self.bindArraySize = space.int_w(w_value) def bindvars_get(space, self): if self.bindList: return space.newlist(self.bindList) if self.bindDict: return self.bindDict def fetchvars_get(space, self): return space.newlist(self.fetchVariables) W_Cursor.typedef = TypeDef( 'Cursor', execute = interp2app(W_Cursor.execute), executemany = interp2app(W_Cursor.executemany), prepare = interp2app(W_Cursor.prepare), fetchone = interp2app(W_Cursor.fetchone), fetchmany = interp2app(W_Cursor.fetchmany), fetchall = interp2app(W_Cursor.fetchall), close = interp2app(W_Cursor.close), bindnames = interp2app(W_Cursor.bindnames), callfunc = interp2app(W_Cursor.callfunc), callproc = interp2app(W_Cursor.callproc), var = interp2app(W_Cursor.var), arrayvar = interp2app(W_Cursor.arrayvar), setinputsizes = interp2app(W_Cursor.setinputsizes), setoutputsize = interp2app(W_Cursor.setoutputsize), __iter__ = interp2app(W_Cursor.descr_iter), next = interp2app(W_Cursor.descr_next), arraysize = GetSetProperty(W_Cursor.arraysize_get, W_Cursor.arraysize_set), bindarraysize = GetSetProperty(W_Cursor.bindarraysize_get, W_Cursor.bindarraysize_set), rowcount = interp_attrproperty('rowCount', W_Cursor), statement = interp_attrproperty_w('w_statement', W_Cursor), bindvars = GetSetProperty(W_Cursor.bindvars_get), fetchvars = GetSetProperty(W_Cursor.fetchvars_get), description = GetSetProperty(W_Cursor.getDescription), )