On 15/01/10 15:14, Charles Jardine wrote:
On 14/01/10 12:19, Charles Jardine wrote:
My environment is Linux x86-64, Perl 5.10.1 (64 bit), DBI 1.609,
Oracle 10.2.0.4.2 (64 bit). Database charset UTF8, national
charset AL16UTF16
Three things:
[ snip ]
2. Back in early October, I reported a problem with the support for
objects. Specifically, fetching a column value which was a nested
table of objects was failing if $dbh->{ora_objects} was true.
I have found the cause of the problem and have developed a patch.
Unfortunately, while investigating this I have found another serious
problem with the object support. Fetching a column value which
is an object, one of whose properties is an object, segfaults,
regardless of the value of $dbh->{ora_objects}. The cause of this
looks like a simple type error.
I hope to be able to post a patch which fixes both these problems
within the next few days.
I have attached a patch which fixes the first problem. I will
post another message about the second problem, which I no longer
see as easy to fix.
Things have move on - I am now at Oracle 10.2.0.4.3.
I have a attached patch for the second problem. In will post
a further patch providing regression tests for both problems.
The table which demonstrates the second problem was set up
as follows:
CREATE OR REPLACE TYPE inner_type AS OBJECT ( v VARCHAR2(255) );
/
CREATE OR REPLACE TYPE outer_type AS OBJECT ( id NUMBER, inner_obj inner_type);
/
CREATE TABLE nest(outer_obj outer_type);
INSERT INTO nest VALUES (outer_type(1, inner_type('Hello world')));
In my environment, fetching the single row of this table causes
a segmentation violation regardless of the value of $dbh->{ora_objects}.
The violation occurs in the call of OCIObjectGetAttr which is
attempting to get the property named V from the inner object.
The cause of the violation is that the 4th argument passed to
OCIObjectGetAttr has the value 0x10000. This argument should be
a pointer to an indicator structure. It is apparently an indicator
value. This value has just been got from the inner object itself
using OCIObjectGetInd. Is seems that OCIObjectGetInd does not always
return a pointer to an indicator structure when it is applied to
an object instance. In particular, it doesn't work in this case
(when the object type is FINAL, and the object instance is a
property of another object).
When OCIObjectGetAttr is used to get the of the inner object from
the instance of the outer object, an indicator structure for
the inner object is returned via the attr_null_struct argument.
The DBD::Oracle code never uses this result, instead calling
OCIObjectGetInd later, to attempt to get the same structure
again.
Poking a little further, I have realised that, whenever get_object
is called, an indicator structure suitable for passing to
OCIObjectGetAttr is already to hand. There is no need for get_object
ever to call OCIObjectGetInd.
There are three cases:
* Objects which are column values:
The indicator structure was obtained at define time via
the indpp argument to OCIDefineObject and stored in
obj->obj_ind.
* Objects which are properties of other objects:
The indicator structure is returned by the existing
call of OCIObjectGetAttr (arg attr_null_struct).
* Objects in varrays or multisets:
The indicator structure is returned by the existing
call of OCIIterNext (arg elemind)
The patch I attach adds an extra argument to get_object,
which always points to an indicator structure obtained
as above. It removes all the calls of OCIObjectGetInd.
It also fixes a bug by testing the return codes of the
two recursive calls of get_object.
The elements obj->obj_ind are now used only to store the
pointers obtained from OCIDefineObject. They are no longer
modified at fetch time.
--
Charles Jardine - Computing Service, University of Cambridge
c...@cam.ac.uk Tel: +44 1223 334506, Fax: +44 1223 334679
Only in DBD-Oracle-1.24-RC3.obj_ind_patch/: Makefile.old
diff -ur DBD-Oracle-1.24-RC3/oci8.c DBD-Oracle-1.24-RC3.obj_ind_patch/oci8.c
--- DBD-Oracle-1.24-RC3/oci8.c 2010-01-08 19:39:46.000000000 +0000
+++ DBD-Oracle-1.24-RC3.obj_ind_patch/oci8.c 2010-01-18 11:22:52.171812759
+0000
@@ -2177,7 +2177,7 @@
/*gets the properties of an object from a fetch by using the attributes saved
in the describe */
int
-get_object (SV *sth, AV *list, imp_fbh_t *fbh,fbh_obj_t
*base_obj,OCIComplexObject *value, OCIType *instance_tdo){
+get_object (SV *sth, AV *list, imp_fbh_t *fbh,fbh_obj_t
*base_obj,OCIComplexObject *value, OCIType *instance_tdo, dvoid *obj_ind){
dTHX;
sword status;
@@ -2191,7 +2191,6 @@
OCIType *attr_tdo;
OCIIter *itr;
fbh_obj_t *fld;
- OCIInd *obj_ind;
fbh_obj_t *obj = base_obj;
OCIType *tdo = instance_tdo ? instance_tdo : obj->tdo;
@@ -2265,18 +2264,6 @@
}
- if (obj->obj_ind) {
- obj_ind = obj->obj_ind;
- } else {
-
-
status=OCIObjectGetInd(fbh->imp_sth->envhp,fbh->imp_sth->errhp,value,(dvoid**)&obj_ind);
-
- if (status != OCI_SUCCESS) {
- oci_error(sth, fbh->imp_sth->errhp,
status, "OCIObjectGetInd");
- return 0;
- }
- }
-
for (pos = 0; pos < obj->field_count; pos++){
fld = &obj->fields[pos]; /*get the field */
@@ -2286,29 +2273,6 @@
av_push(list,
newSVpv((char*)fld->type_name, fld->type_namel));
}
-
status=OCIObjectGetInd(fbh->imp_sth->envhp,fbh->imp_sth->errhp,value,(dvoid**)&obj->obj_ind);
-/*
-the little bastard above took me ages to find out
-seems Oracle does not like people to know that it can do this
-the concept is simple really
- 1. pin the object
- 2. bind with dty = SQLT_NTY
- 3. OCIDefineObject using the TDO
- 4. one gets the null indicator of the objcet with OCIObjectGetInd
- The the obj_ind is for the entier object not the properties so you call
it once it
- gets all of the indicators for the objects so you pass it into
OCIObjectGetAttr and that
- function will set attr_null_status as in the get below.
- 5. interate over the atributes of the object
-
-The thing to remember is that OCI and C have no way of representing a DB NULLs
so we use the OCIInd find out
-if the object or any of its properties are NULL, This is one little line in a
20 chapter book and even then
-id only shows you examples with the C struct built in and only a single
record. Nowhere does it say you can do it this way.
-*/
- if (status != OCI_SUCCESS) {
- oci_error(sth, fbh->imp_sth->errhp,
status, "OCIObjectGetInd");
- return 0;
- }
-
status = OCIObjectGetAttr(fbh->imp_sth->envhp,
fbh->imp_sth->errhp, value,
obj_ind, tdo,
(CONST oratext**)&fld->type_name, &fld->type_namel, 1,
@@ -2329,7 +2293,7 @@
if (fld->typecode !=
OCI_TYPECODE_OBJECT)
attr_value = *(dvoid
**)attr_value;
- get_object
(sth,fld->fields[0].value, fbh, &fld->fields[0],attr_value, attr_tdo);
+ if (!get_object
(sth,fld->fields[0].value, fbh, &fld->fields[0],attr_value, attr_tdo,
attr_null_struct)) return 0;
av_push(list,
new_ora_object(fld->fields[0].value, fld->typecode));
} else{ /* else, display the scaler
type attribute */
@@ -2370,7 +2334,7 @@
} else {
if
(obj->element_typecode == OCI_TYPECODE_OBJECT || obj->element_typecode ==
OCI_TYPECODE_VARRAY || obj->element_typecode== OCI_TYPECODE_TABLE ||
obj->element_typecode== OCI_TYPECODE_NAMEDCOLLECTION){
fld->value =
newAV();
- get_object
(sth,fld->value, fbh, fld,element,0);
+ if(!get_object
(sth,fld->value, fbh, fld,element,0,element_null)) return 0;
av_push(list,
new_ora_object(fld->value, obj->element_typecode));
} else{ /* else,
display the scaler type attribute */
get_attr_val(sth,list, fbh, obj->type_name, obj->element_typecode, element);
@@ -2421,7 +2385,7 @@
fbh->obj->value=newAV();
/*will return referance to an array of scalars*/
- if
(!get_object(sth,fbh->obj->value,fbh,fbh->obj,fbh->obj->obj_value,0)){
+ if
(!get_object(sth,fbh->obj->value,fbh,fbh->obj,fbh->obj->obj_value,0,fbh->obj->obj_ind)){
return 0;
} else {
sv_setsv(dest_sv, sv_2mortal(new_ora_object(fbh->obj->value,
fbh->obj->typecode)));