AW: [Zope-DB] [ANN] Modified version of DCOracle2 is available

Happle Dr., Klaus Martin Klaus.Happle at haufe.de
Thu Nov 23 12:59:03 EST 2006


Hi

you remember my report 

http://mail.zope.org/pipermail/zope/2005-August/160762.html

of an BUG for the handling of LONGs in DCO2?

The consequence of this BUG is stochastic results for LONG fields

We use an Fix of this BUG:

Hint: The Documentation of the OCI from Oracle say us:

	defnp (IN), iter (IN), bufpp (OUT), alenpp (IN/OUT), piecep (IN/OUT), indpp (IN), rcodep (IN)

	Caution: When working with callback parameters, it is important
	to keep in mind what is meant by IN and OUT for the parameter
	mode. Normally, in an OCI function, an IN parameter refers to data
	being passed to Oracle, and an OUT parameter refers to data
	coming back from Oracle. In the case of callbacks, this is reversed.
	IN means data is coming from Oracle into the callback, and OUT
	means data is coming out of the callback and going to Oracle.

		Docu from OCI for OCIDefineByPos:
		indp (IN/OUT), alenp (IN/OUT), rcodep (OUT): Ignored for dynamic binds.

First we fix Cursor_ResultSet and Second we fix Cursor_fetch:

First Fix:

static PyObject *Cursor_ResultSet(Cursor *self, int count) {
	PyObject *list;
	ResultSet *rs;
	int status;
	int i;
	sword mode = OCI_DEFAULT;
	dvoid *valuep;
	ub4 width;
	LongFetch *lf;

	TRACE(T_ENTRY,("sAd", "Cursor_ResultSet", self, count));

	if (self->definition == NULL) {
		TRACE(T_ERROR,("ss","Cursor_ResultSet","description is NULL"));
		PyErr_SetString(ProgrammingErrorObject,
			"cursor description is None");
		return NULL;
	}

	self->batchsz = count;

	if ((list = Py_BuildValue("[]")) == NULL) {
		TRACE(T_ERROR,("ss","Cursor_ResultSet",
			"PyBuildValue returned NULL"));
		return NULL;
	}

	for (i = 1; i <= PyList_Size(self->definition); i++) {
		mode = OCI_DEFAULT;
		if ((rs = (ResultSet *) ResultSet_alloc(self, i, count))
			== NULL) {

			Py_DECREF(list);
			TRACE(T_ERROR,("ss","Cursor_ResultSet",
				"ResultSetAlloc returned NULL"));
			return NULL;
		}

		valuep = rs->valuep;
		width = rs->width;
		rs->fetchResultCode = OCI_SUCCESS;

		if (self->flags & LONG_COLUMN && (char) i == self->longcol) {
			mode = OCI_DYNAMIC_FETCH;
			lf = (LongFetch *) rs->valuep;
			longFetchInit(lf);
			/*valuep = NULL;*/
			width = 0x7FFFFFFF; /* Max unsigned long */
			rs->indp = &lf->ind; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
			rs->rcodep = &lf->rcode; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
		}

		TRACE(T_CALL,("sdAddd", "OCIDefineByPos", i, valuep, width,
			rs->cdty, mode));

		/* Now bind the result set */
		/*
		Docu from OCI:
		indp (IN/OUT), alenp (IN/OUT), rcodep (OUT): Ignored for dynamic binds.
		*/
		status = OCIDefineByPos(self->stmtp, &(rs->defnp),
			self->errhp, i, valuep, width,
			rs->cdty, (dvoid *) rs->indp,
			rs->rlenp, rs->rcodep, mode);

		TRACE(T_RETURN,("sR", "OCIDefineByPos", status));
			
		if (status != OCI_SUCCESS) {
			Py_DECREF(rs);
			Py_DECREF(list);
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		}

		if (self->flags & LONG_COLUMN && (char) i == self->longcol) {

			TRACE(T_CALL,("sA", "OCIDefineDynamic", rs->valuep));

			status = OCIDefineDynamic(rs->defnp, self->errhp,
				(dvoid *) rs->valuep,
				(OCICallbackDefine) dynamicFetch);

			TRACE(T_RETURN,("sR", "OCIDefineDynamic", status));

			if (status != OCI_SUCCESS) {
				Py_DECREF(rs);
				Py_DECREF(list);
				return RaiseOCIError(self->errhp,
					OCI_HTYPE_ERROR);
			}
		}

		PyList_Append(list, OBJECT(rs));
		Py_DECREF(rs); /* Now that its in the list ... */
	}

	if (self->results != NULL) {
		Py_DECREF(self->results);
	}
		
	self->results = list;
	self->current = 0;

	Py_INCREF(Py_None);

	TRACE(T_EXIT,("s","Cursor_ResultSet"));

	return Py_None;
}

Second Fix:

static PyObject *Cursor_fetch(Cursor *self, PyObject *args) {
	int count = 1;
	ub4 rcount;
	int status;
	ub2 *rlenp;
	ResultSet *rs;
	int i;
	int j;
	int resetlong = 0;
	int releaseresults = 0;
	PyObject *answer;

	TRACE(T_ENTRY,("sAA", "Cursor_fetch", self, args));

	/* bindObject may have bound this cursor, in which case we
	** must refetch the description
	*/
	if (self->flags & CURSOR_INSPECT) 
		Cursor_getdesc(self);

	if (!PyArg_ParseTuple(args, "|i", &count)) return NULL;

	TRACE(T_ARGS,("sd", "Cursor_fetch", count));

	if (count < 1 || count > 200) {
		PyErr_SetString(PyExc_ValueError, "Count out of range");
		return NULL;
	}

	if (self->flags & LONG_COLUMN) {
		count = 1;		/* Should we raise an error? */

		if (self->current != -1)
			resetlong = 1;
	}

	
	if (self->flags & NO_RESULT) {
		TRACE((T_PROGRAM|T_ERROR),("ss","Cursor_fetch",
			"no result set from execute"));
		PyErr_SetObject(ProgrammingErrorObject,
			Py_BuildValue("s",
			"No results available from last execute operation"));
		return NULL;
	}

	if (self->current == -1) 
		if (Cursor_ResultSet(self, count) == NULL) return NULL;

	/* Set the result widths */

	for (i = 0; i < PyList_Size(self->results); i++) {
		if ((rs = (ResultSet *) PyList_GetItem(self->results, i)) 
						== NULL) return NULL;

		rlenp = rs->rlenp;
		for (j = 0; j < rs->size; j++) {
			*rlenp = (ub2) rs->width;
			rlenp++;
		}

		if (resetlong == 1 && (i+1) == self->longcol) {
			LongFetch *lf = (LongFetch *) rs->valuep;
			longFetchRelease(lf);
			longFetchInit(lf);
			rs->indp = &lf->ind; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
			rs->rcodep = &lf->rcode; //KMH, 2.8.2005 synchronisation of dynamicFetch with ResultSet
		}
	}


	TRACE(T_CALL,("sd", "OCIStmtFetch", count));

	Py_BEGIN_ALLOW_THREADS

	/*
	** NB: A fetch of length 0 will cancel the cursor
	**
	** Do we *need* to cancel the cursor when the Cursor is
	** deallocated or will Oracle figure it out when the statement
	** handle is released?
	*/

	status = OCIStmtFetch(self->stmtp, self->errhp, count,
		OCI_FETCH_NEXT, OCI_DEFAULT);

	Py_END_ALLOW_THREADS

	TRACE(T_RETURN,("sR", "OCIStmtFetch", status));

	/*
	** Copy the fetch status into each result
	*/
	if (status == OCI_SUCCESS_WITH_INFO) {
		for (i = 0; i < PyList_Size(self->results); i++) {
			rs = (ResultSet *) PyList_GetItem(self->results, i);
			rs->fetchResultCode=status;
		}
	}
	if (status == OCI_SUCCESS) {
		for (i = 0; i < PyList_Size(self->results); i++) {
			rs = (ResultSet *) PyList_GetItem(self->results, i);
			rs->fetchResultCode=status;
		}
	}
	TRACE(T_VERBOSE,("sd", "Cursor_fetch status", status));

	if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) {
		text buff[OCI_ERROR_MAXMSG_SIZE];
		sb4	errcode;

		OCIErrorGet(self->errhp, 1, NULL, &errcode, buff,
			sizeof(buff), OCI_HTYPE_ERROR);
		/* Oracle errors meaning "end of fetch" 
		** 1403 is 
		** 1002 is fetch out of sequence
		*/

		TRACE(T_OERROR,("sdS","OCIStmtFetch",errcode,buff));

		if (errcode != 1403 && errcode != 1002) {/* Not End of data */
			Py_DECREF(self->results);
			self->results = NULL;
			self->current = -1;
			return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);
		}
		releaseresults = 1;
	}

	TRACE(T_HCALL,("sAsAs", "OCIAttrGet", self->stmtp,
		"OCI_HTYPE_STMT", &rcount, "OCI_ATTR_ROW_COUNT"));
	status = OCIAttrGet((dvoid *) self->stmtp, OCI_HTYPE_STMT,
		(ub4 *) &rcount, 0, OCI_ATTR_ROW_COUNT,
		self->errhp);
	TRACE(T_HRETURN,("sRd", "OCIAttrGet", status, rcount));

	if (status == OCI_SUCCESS) {
		if (self->batchct == -1) self->batchct = 0;
		self->count = rcount - self->batchct;
		self->batchct = rcount;
	} else	
		return RaiseOCIError(self->errhp, OCI_HTYPE_ERROR);

	answer = self->results;
	if (!releaseresults)
		Py_INCREF(answer);
	else {
		self->results = NULL;	/* Our caller gets our only copy*/
		self->current = -1;
	}

	TRACE(T_EXIT,("sA", "Cursor_fetch", answer));

	return answer;

}


Klaus Happle


-----Ursprüngliche Nachricht-----
Von: zope-db-bounces at zope.org [mailto:zope-db-bounces at zope.org] Im Auftrag von Maciej Wisniowski
Gesendet: Freitag, 17. November 2006 17:32
An: Zope-DB at zope.org
Betreff: [Zope-DB] [ANN] Modified version of DCOracle2 is available


Hi

Due to discussion on Zope list and some work I did before
I've published modified version of DCOracle2.

What is in this version:
1. Bug fixes
 - StoredProcedures caused deadlocks in database
 - StoredProcedures caused conflict errors under heavy load
   removed self._errors reference and added _p_resolveConflict
 - other I don't remember now

2. Changes
 Changes are only in python files
 - DCOracle2 now uses connection pool
   Pool implementation is based on psycopg but
   it doesn't use volatile attributes.

   As a result:
   - Open/Close button in ZMI works as it should
   - DCOracle is possibly slower a bit

 - Ability to reconnect after the connection is broken
   When Oracle is restarted, connection is closed by firewall
   etc. Zope will show an error but only once for every broken
   connection. Second request will cause DCOracle2 to reconnect

3. To do:
 - Possibly useage of raise ConflictError may cause that there
   will be no errors shown during reconnect

 - Pool size attribute may be set via ZMI

 - Testing...

This code is now in use at my company, but not yet in production
environment. So far this works with Zope 2.8.x and Oracle9.

If somebody knows better place for this code then let me know.
Any ideas how should file headers (I mean licences) look?
Comments are welcome

Code is here:

http://code.google.com/p/dcoracle2da/

Should be available via svn with:

svn checkout http://dcoracle2da.googlecode.com/svn/trunk/ dcoracle2da

THERE IS NO GUARANTEE THAT THIS WILL WORK FOR YOU, SO BE CAREFULL :)

-- 
Maciej Wisniowski

_______________________________________________
Zope-DB mailing list
Zope-DB at zope.org
http://mail.zope.org/mailman/listinfo/zope-db


More information about the Zope-DB mailing list