Author: byterock
Date: Wed Jan 14 07:28:59 2009
New Revision: 12438
Added:
dbd-oracle/trunk/Object.pm
dbd-oracle/trunk/t/58object.t
Modified:
dbd-oracle/trunk/Changes
dbd-oracle/trunk/Oracle.pm
dbd-oracle/trunk/dbdimp.c
dbd-oracle/trunk/dbdimp.h
dbd-oracle/trunk/oci8.c
dbd-oracle/trunk/ocitrace.h
Log:
Added rt.cpan.org Ticket #=42328 ora_objects attribute for extended embedded
objects support from tnt at netsafe.cz
Fix for rt.cpan.org Ticket #=42328 user defined types from different schema
in describe_obj from tnt at netsafe.cz
Modified: dbd-oracle/trunk/Changes
==============================================================================
--- dbd-oracle/trunk/Changes (original)
+++ dbd-oracle/trunk/Changes Wed Jan 14 07:28:59 2009
@@ -1,4 +1,6 @@
=head1 Changes in DBD-Oracle 1.23(svn rev #####)
+ Added rt.cpan.org Ticket #=42328 ora_objects attribute for extended embedded
objects support from tnt at netsafe.cz
+ Fix for rt.cpan.org Ticket #=42328 user defined types from different schema
in describe_obj from tnt at netsafe.cz
Added a README for sun suggested by Jim McCullars
Clean up of white space and formating to 4 tabs from John Scoles
Fix for GCC 4.3 warnings from Eric Simon
Added: dbd-oracle/trunk/Object.pm
==============================================================================
--- (empty file)
+++ dbd-oracle/trunk/Object.pm Wed Jan 14 07:28:59 2009
@@ -0,0 +1,24 @@
+package DBD::Oracle::Object;
+
+use strict;
+use warnings;
+
+sub type_name { shift->{type_name} }
+
+sub attributes { @{shift->{attributes}} }
+
+sub attr_hash {
+ my $self = shift;
+ return $self->{attr_hash} ||= { $self->attributes };
+}
+
+sub attr {
+ my $self = shift;
+ if (@_) {
+ my $key = shift;
+ return $self->attr_hash->{$key};
+ }
+ return $self->attr_hash;
+}
+
+1;
\ No newline at end of file
Modified: dbd-oracle/trunk/Oracle.pm
==============================================================================
--- dbd-oracle/trunk/Oracle.pm (original)
+++ dbd-oracle/trunk/Oracle.pm Wed Jan 14 07:28:59 2009
@@ -17,6 +17,8 @@
use DBI ();
use DynaLoader ();
use Exporter ();
+ use Object;
+
@ISA = qw(DynaLoader Exporter);
%EXPORT_TAGS = (
ora_types => [ qw(
@@ -319,7 +321,8 @@
ora_ncharset => undef,
ora_session_mode => undef,
ora_verbose => undef,
- ora_oci_success_warn => undef
+ ora_oci_success_warn => undef,
+ ora_objects => undef
};
}
@@ -1519,6 +1522,24 @@
$dbh->{ora_oci_success_warn} =1;
+=item ora_objects
+
+Use this value to enable extended embedded oracle objects mode. In extended:
+
+=over 8
+
+=item 1
+
+Embedded objects are returned as <DBD::Oracle::Object> instance (including
type-name etc.) instead of simple ARRAY.
+
+=item 2
+
+Determine object type for each instance. All object attributes are returned
(not only super-type's attributes).
+
+=back
+
+ $dbh->{ora_objects} = 1;
+
=item ora_ph_type
The default placeholder data type for the database session.
Modified: dbd-oracle/trunk/dbdimp.c
==============================================================================
--- dbd-oracle/trunk/dbdimp.c (original)
+++ dbd-oracle/trunk/dbdimp.c Wed Jan 14 07:28:59 2009
@@ -43,6 +43,8 @@
int is_extproc = 0;
int dbd_verbose = 0; /* DBD only debugging*/
int oci_warn = 0; /* show oci warnings */
+int ora_objects = 0; /* get oracle embedded objects as instance of
DBD::Oracle::Object */
+
/* bitflag constants for figuring out how to handle utf8 for array binds */
#define ARRAY_BIND_NATIVE 0x01
#define ARRAY_BIND_UTF8 0x02
@@ -103,7 +105,7 @@
if (*(SvEND(errstr)-1) == '\n')
--SvCUR(errstr);
}
-
+
if (what || status != OCI_ERROR) {
sv_catpv(errstr, (debug<0) ? " (" : " (DBD ");
sv_catpv(errstr, oci_status_name(status));
@@ -379,7 +381,8 @@
DBD_ATTRIB_GET_IV( attr, "ora_verbose", 11, svp, dbd_verbose);
if (DBD_ATTRIB_TRUE(attr,"ora_oci_success_warn",20,svp))
DBD_ATTRIB_GET_IV( attr, "ora_oci_success_warn", 20, svp,
oci_warn);
-
+ if (DBD_ATTRIB_TRUE(attr,"ora_objects",11,svp))
+ DBD_ATTRIB_GET_IV( attr, "ora_objects",11, svp, ora_objects);
/* dbi_imp_data code adapted from DBD::mysql */
if (DBIc_has(imp_dbh, DBIcf_IMPSET)) {
@@ -954,6 +957,9 @@
if (kl==20 && strEQ(key, "ora_oci_success_warn") ) {
oci_warn = SvIV (valuesv);
}
+ else if (kl==11 && strEQ(key, "ora_objects")) {
+ ora_objects = SvIV (valuesv);
+ }
else if (kl==11 && (strEQ(key, "ora_verbose") || strEQ(key,
"dbd_verbose"))) {
dbd_verbose = SvIV (valuesv);
}
@@ -989,7 +995,7 @@
if (cacheit) /* cache value for later DBI 'quick' fetch? */
(void)hv_store((HV*)SvRV(dbh), key, kl, newSVsv(valuesv), 0);
-
+
return TRUE;
}
@@ -1009,6 +1015,9 @@
if (kl==20 && strEQ(key, "ora_oci_success_warn")) {
retsv = newSViv (oci_warn);
}
+ else if (kl==11 && strEQ(key, "ora_objects")) {
+ retsv = newSViv (ora_objects);
+ }
else if (kl==11 && (strEQ(key, "ora_verbose") || strEQ(key,
"dbd_verbose"))) {
retsv = newSViv (dbd_verbose);
}
@@ -1230,28 +1239,28 @@
dest = start+strlen(start);
style = "?";
- }
+ }
else if (isDIGIT(*src)) { /* ':1' */
idx = atoi(src);
*dest++ = 'p'; /* ':1'->':p1' */
if (idx <= 0)
croak("Placeholder :%d invalid, placeholders
must be >= 1", idx);
-
+
while(isDIGIT(*src))
*dest++ = *src++;
style = ":1";
- }
+ }
else if (isALNUM(*src)) { /* ':foo' */
while(isALNUM(*src)) /* includes '_' */
*dest++ = toLOWER(*src), src++;
style = ":foo";
-
+
} else { /* perhaps ':=' PL/SQL
construct */
/* if (src == ':') *dest++ = *src++; XXX? move past
'::'? */
continue;
}
-
+
*dest = '\0'; /* handy for debugging */
namelen = (dest-start);
if (laststyle && style != laststyle)
@@ -2450,7 +2459,7 @@
}
return 1;
-
+
}
static int
@@ -3849,7 +3858,7 @@
while(--i >= 0)
av_store(av, i, newSVpv((char*)imp_sth->fbh[i].name,0));
- }
+ }
else if (kl==11 && strEQ(key, "ParamValues")) {
HV *pvhv = newHV();
if (imp_sth->all_params_hv) {
@@ -3865,55 +3874,55 @@
retsv = newRV_noinc((SV*)pvhv);
cacheit = FALSE;
- }
+ }
else if (kl==11 && strEQ(key, "ora_lengths")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, newSViv((IV)imp_sth->fbh[i].disize));
- }
+ }
else if (kl==9 && strEQ(key, "ora_types")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, newSViv(imp_sth->fbh[i].dbtype));
- }
+ }
else if (kl==4 && strEQ(key, "TYPE")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i,
newSViv(ora2sql_type(imp_sth->fbh+i).dbtype));
- }
+ }
else if (kl==5 && strEQ(key, "SCALE")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i,
newSViv(ora2sql_type(imp_sth->fbh+i).scale));
- }
+ }
else if (kl==9 && strEQ(key, "PRECISION")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i,
newSViv(ora2sql_type(imp_sth->fbh+i).prec));
#ifdef XXX
- }
+ }
else if (kl==9 && strEQ(key, "ora_rowid")) {
/* return current _binary_ ROWID (oratype 11) uncached */
/* Use { ora_type => 11 } when binding to a placeholder */
retsv = newSVpv((char*)&imp_sth->cda->rid,
sizeof(imp_sth->cda->rid));
cacheit = FALSE;
#endif
- }
+ }
else if (kl==17 && strEQ(key, "ora_est_row_width")) {
retsv = newSViv(imp_sth->est_width);
cacheit = TRUE;
- }
+ }
else if (kl==8 && strEQ(key, "NULLABLE")) {
AV *av = newAV();
retsv = newRV(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, boolSV(imp_sth->fbh[i].nullok));
- }
+ }
else {
return Nullsv;
}
Modified: dbd-oracle/trunk/dbdimp.h
==============================================================================
--- dbd-oracle/trunk/dbdimp.h (original)
+++ dbd-oracle/trunk/dbdimp.h Wed Jan 14 07:28:59 2009
@@ -152,7 +152,10 @@
OCIType *obj_type; /*if an embeded
object this is the OCIType returned by a OCIObjectPin*/
fbh_obj_t *fields; /*one object
for each field/property*/
int field_count; /*The number of
fields Not really needed but nice to have*/
+ fbh_obj_t *next_subtype; /*There is strored
information about subtypes for inteherited objects*/
AV *value; /*The
value to send back to Perl This way there are no memory leaks*/
+ SV *full_type_name; /*Perl value of
full type name = schema_name "." type_name*/
+
};
struct imp_fbh_st { /* field buffer EXPERIMENTAL */
@@ -251,6 +254,7 @@
extern int ora_fetchtest;
extern int dbd_verbose;
extern int oci_warn;
+extern int ora_objects;
extern ub2 charsetid;
extern ub2 ncharsetid;
Modified: dbd-oracle/trunk/oci8.c
==============================================================================
--- dbd-oracle/trunk/oci8.c (original)
+++ dbd-oracle/trunk/oci8.c Wed Jan 14 07:28:59 2009
@@ -19,6 +19,9 @@
DBISTATE_DECLARE;
+int describe_obj_by_tdo(SV *sth,imp_sth_t *imp_sth,fbh_obj_t *obj,int level );
+int dump_struct(imp_sth_t *imp_sth,fbh_obj_t *obj,int level);
+
void
@@ -567,11 +570,10 @@
DBD_ATTRIB_GET_IV( attribs, "ora_prefetch_memory", 19, svp,
imp_sth->prefetch_memory);
DBD_ATTRIB_GET_IV( attribs, "ora_verbose", 11, svp,
dbd_verbose);
DBD_ATTRIB_GET_IV( attribs, "ora_oci_success_warn", 20, svp,
oci_warn);
+ DBD_ATTRIB_GET_IV( attribs, "ora_objects", 11, svp,
ora_objects);
- if (!dbd_verbose)
- DBD_ATTRIB_GET_IV( attribs, "dbd_verbose",
11, svp, dbd_verbose);
-
-
+ if (!dbd_verbose)
+ DBD_ATTRIB_GET_IV( attribs, "dbd_verbose", 11, svp,
dbd_verbose);
}
@@ -656,7 +658,7 @@
AV *av;
SV **sv_p;
if( bindp ){ /* For GCC not to warn on unused parameter*/ }
-
+
tuples_av = phs->imp_sth->bind_tuples;
if(tuples_av) {
/* NOTE: we already checked the validity in
ora_st_bind_for_array_exec(). */
@@ -668,7 +670,7 @@
*bufpp = SvPV(sv, phs_len);
phs->alen = (phs->alen_incnull) ? phs_len+1 : phs_len;
phs->indp = 0;
- }
+ }
else {
*bufpp = SvPVX(sv);
phs->alen = 0;
@@ -765,7 +767,7 @@
*bufpp = phs->desc_h;
phs->alen = 0;
- }
+ }
else {
SV *sv = phs->sv;
@@ -1007,15 +1009,15 @@
imp_sth_nested->errhp = imp_sth->errhp;
imp_sth_nested->srvhp = imp_sth->srvhp;
imp_sth_nested->svchp = imp_sth->svchp;
-
+
imp_sth_nested->stmhp = stmhp_nested;
imp_sth_nested->nested_cursor = 1;
imp_sth_nested->rs_array_on = 1;
imp_sth_nested->stmt_type = OCI_STMT_SELECT;
-
+
DBIc_IMPSET_on(imp_sth_nested);
DBIc_ACTIVE_on(imp_sth_nested); /* So describe won't do an
execute */
-
+
if (!dbd_describe(dest_sv, imp_sth_nested))
return 0;
}
@@ -1090,7 +1092,7 @@
if (SvOK(phs->sv)) { /* ie a number, convert to string ASAP
*/
if (!(SvROK(phs->sv) && phs->is_inout))
sv_2pv(phs->sv, &na);
- }
+ }
else { /* ensure we're at least an SVt_PV (so SvPVX etc work)
*/
if(SvUPGRADE(phs->sv, SVt_PV)){} /* For GCC not to warn
on unused result */
}
@@ -1122,7 +1124,7 @@
ub4 amtp;
if(SvUPGRADE(phs->sv, SVt_PV)){/* For GCC not to warn on unused
result */}; /* just in case */
-
+
amtp = SvCUR(phs->sv); /* XXX UTF8? */
/* Create a temp lob for non-empty string */
@@ -1754,10 +1756,27 @@
}
}
+
+SV* new_ora_object (AV* list, OCITypeCode typecode) {
+ dTHX;
+ SV* objref = newRV_noinc((SV*) list);
+
+ if (ora_objects && typecode == OCI_TYPECODE_OBJECT) {
+ HV* self = newHV();
+ (void)hv_store(self, "type_name", 9, av_shift(list), 0);
+ (void)hv_store(self, "attributes", 10, objref, 0);
+ objref = newRV_noinc((SV*) self);
+ objref = sv_bless(objref, gv_stashpv("DBD::Oracle::Object", 0));
+
+ }
+ return objref;
+}
+
/*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 *obj,OCIComplexObject
*value){
+get_object (SV *sth, AV *list, imp_fbh_t *fbh,fbh_obj_t
*base_obj,OCIComplexObject *value){
+
dTHX;
sword status;
dvoid *element ;
@@ -1771,6 +1790,7 @@
OCIIter *itr;
fbh_obj_t *fld;
OCIInd *obj_ind;
+ fbh_obj_t *obj = base_obj;
if (DBIS->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBILOGFP, " getting attributes of object named
%s with typecode=%s\n",obj->type_name,oci_typecode_name(obj->typecode));
@@ -1780,12 +1800,72 @@
case OCI_TYPECODE_OBJECT :
/* embedded ADT */
+ if (ora_objects){
+
+ OCIRef *type_ref=0;
+ sword status;
+ OCIType *tdo;
+
+ status = OCIObjectNew(fbh->imp_sth->envhp,
fbh->imp_sth->errhp, fbh->imp_sth->svchp,
+
OCI_TYPECODE_REF, (OCIType *)0,
+
(dvoid *)0, OCI_DURATION_DEFAULT, TRUE,
+
(dvoid **) &type_ref);
+ if (status != OCI_SUCCESS) {
+ oci_error(sth, fbh->imp_sth->errhp,
status, "OCIObjectNew");
+ return 0;
+ }
+
+
status=OCIObjectGetTypeRef(fbh->imp_sth->envhp,fbh->imp_sth->errhp,
(dvoid*)fbh->obj->obj_value, type_ref);
+ if (status != OCI_SUCCESS) {
+ oci_error(sth, fbh->imp_sth->errhp,
status, "OCIObjectGetTypeRef");
+ return 0;
+ }
+
+
OCITypeByRef_log_stat(fbh->imp_sth->envhp,fbh->imp_sth->errhp,type_ref,&tdo,status);
+
+ if (status != OCI_SUCCESS) {
+ oci_error(sth, fbh->imp_sth->errhp,
status, "OCITypeByRef");
+ return 0;
+ }
+
+ if (tdo != obj->tdo) {
+ /* this is subtype -> search for
subtype obj */
+ while (obj->next_subtype && tdo !=
obj->tdo) {
+ obj = obj->next_subtype;
+ }
+ if (tdo != obj->tdo) {
+ /* new subtyped -> get obj
description */
+ if (DBIS->debug >= 5 ||
dbd_verbose >= 5 ) {
+ PerlIO_printf(DBILOGFP,
" describe subtype of object type %s\n",base_obj->type_name);
+ }
+
+ Newz(1, obj->next_subtype, 1,
fbh_obj_t);
+ obj->next_subtype->tdo = tdo;
+ if ( describe_obj_by_tdo(sth,
fbh->imp_sth, obj->next_subtype, 0 /*unknown level there*/) ) {
+ obj = obj->next_subtype;
+ if (DBIS->debug >= 5 ||
dbd_verbose >= 5 ){
+
dump_struct(fbh->imp_sth,obj,0);
+ }
+ }
+ else {
+ obj->next_subtype = 0;
+ }
+ }
+
+ if (DBIS->debug >= 5 || dbd_verbose >=
5 ) {
+ PerlIO_printf(DBILOGFP, "
getting attributes of object subtype %s\n",obj->type_name);
+ }
+ }
+
+ av_push(list, newSVpv((char*)obj->type_name,
obj->type_namel));
+ }
+
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;
@@ -1793,7 +1873,13 @@
}
for (pos = 0; pos < obj->field_count; pos++){
- fld = &obj->fields[pos]; /*get the field */
+
+ fld = &obj->fields[pos]; /*get the field */
+
+ if (ora_objects) {
+ /* add field name */
+ 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);
/*
@@ -1837,8 +1923,9 @@
fld->fields[0].value = newAV();
if (fld->typecode !=
OCI_TYPECODE_OBJECT)
attr_value = *(dvoid
**)attr_value;
+
get_object
(sth,fld->fields[0].value, fbh, &fld->fields[0],attr_value);
- av_push(list, newRV_noinc((SV
*) fld->fields[0].value));
+ av_push(list,
new_ora_object(fld->fields[0].value, fld->typecode));
} else{ /* else, display the scaler
type attribute */
@@ -1872,14 +1959,14 @@
(dvoid **) &element,
(dvoid **) &element_null, &eoc)
&& !eoc;)
{
-
+
if
(*element_null==OCI_IND_NULL){
av_push(list,
&sv_undef);
} 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);
- av_push(list,
newRV_noinc((SV *) fld->value));
+ get_object
(sth,fld->value, fbh, fld,element);
+ 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);
}
@@ -1932,7 +2019,7 @@
if (!get_object(sth,fbh->obj->value,fbh,fbh->obj,fbh->obj->obj_value)){
return 0;
} else {
- sv_setsv(dest_sv, sv_2mortal(newRV_noinc((SV *)
fbh->obj->value)));
+ sv_setsv(dest_sv, sv_2mortal(new_ora_object(fbh->obj->value,
fbh->obj->typecode)));
return 1;
}
@@ -2080,18 +2167,22 @@
case OCI_TYPECODE_OBJECT : /* embedded ADT
*/
+ if (obj->next_subtype) {
+ empty_oci_object(obj->next_subtype);
+ }
+
for (pos = 0; pos < obj->field_count; pos++){
- fld = &obj->fields[pos]; /*get the field */
- if (fld->typecode == OCI_TYPECODE_OBJECT ||
fld->typecode == OCI_TYPECODE_VARRAY || fld->typecode == OCI_TYPECODE_TABLE ||
fld->typecode == OCI_TYPECODE_NAMEDCOLLECTION){
- empty_oci_object(fld);
- if (fld->value && SvTYPE(fld->value) ==
SVt_PVAV){
- av_clear(fld->value);
- av_undef(fld->value);
- }
+ fld = &obj->fields[pos]; /*get the
field */
+ if (fld->typecode ==
OCI_TYPECODE_OBJECT || fld->typecode == OCI_TYPECODE_VARRAY || fld->typecode ==
OCI_TYPECODE_TABLE || fld->typecode == OCI_TYPECODE_NAMEDCOLLECTION){
+ empty_oci_object(fld);
+ if (fld->value &&
SvTYPE(fld->value) == SVt_PVAV){
+ av_clear(fld->value);
+ av_undef(fld->value);
+ }
- } else {
- return 1;
- }
+ } else {
+ return 1;
+ }
}
break;
@@ -2160,10 +2251,10 @@
if (imp_sth->rs_array_on!=1 ||
imp_sth->rs_array_size<1 ||
imp_sth->rs_array_size>128){
-
+
imp_sth->rs_array_on=0;
imp_sth->rs_array_size=1;
-
+
}
imp_sth->rs_array_num_rows=0;
imp_sth->rs_array_idx=0;
@@ -2194,7 +2285,7 @@
/* number of rows to cache if using oraperl */
if (SvOK(imp_drh->ora_cache_o)){
imp_sth->cache_rows = SvIV(imp_drh->ora_cache_o);
- }
+ }
else if (SvOK(imp_drh->ora_cache)){
imp_sth->cache_rows = SvIV(imp_drh->ora_cache);
}
@@ -2271,41 +2362,49 @@
{
dTHX;
sword status;
+ OCIRef *type_ref;
if (DBIS->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBILOGFP, "At level=%d in description an embedded
object \n",level);
}
/*Describe the field (OCIParm) we know it is a object or a collection */
- OCIAttrGet_parmdp(imp_sth,parm, &obj->type_name, &obj->type_namel,
OCI_ATTR_TYPE_NAME, status);
- /*get its name and hence TDO*/
- /*Now get the Actual TDO */
+ /* Get the Actual TDO */
+ OCIAttrGet_parmdp(imp_sth,parm, &type_ref, 0, OCI_ATTR_REF_TDO, status);
if (status != OCI_SUCCESS) {
- oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
+ oci_error(sth, imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
- if (DBIS->debug >= 6 || dbd_verbose >= 6 ) {
- PerlIO_printf(DBILOGFP, "Geting the properties of object named
=%s at level %d\n",obj->type_name,level);
- }
-
-
OCITypeByName_log_stat(imp_sth->envhp,imp_sth->errhp,imp_sth->svchp,obj->type_name,obj->type_namel,&obj->tdo,status);
-
+
OCITypeByRef_log_stat(imp_sth->envhp,imp_sth->errhp,type_ref,&obj->tdo,status);
+
if (status != OCI_SUCCESS) {
- oci_error(sth, imp_sth->errhp, status, "OCITypeByName");
+ oci_error(sth, imp_sth->errhp, status, "OCITypeByRef");
return 0;
}
+
+ return describe_obj_by_tdo(sth, imp_sth, obj, level);
+ }
+
+int
+describe_obj_by_tdo(SV *sth,imp_sth_t *imp_sth,fbh_obj_t *obj,int level ) {
+ dTHX;
+ sword status;
+ text *type_name, *schema_name;
+ ub4 type_namel, schema_namel;
+
+
OCIDescribeAny_log_stat(imp_sth->svchp,imp_sth->errhp,obj->tdo,(ub4)0,OCI_OTYPE_PTR,(ub1)1,OCI_PTYPE_TYPE,imp_sth->dschp,status);
/*we have the Actual TDO so lets see what it is made up of by a
describe*/
-
+
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIDescribeAny");
return 0;
}
OCIAttrGet_parmap(imp_sth, imp_sth->dschp,OCI_HTYPE_DESCRIBE,
&obj->parmdp, 0, status);
-
+
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
@@ -2313,15 +2412,39 @@
/*and we store it in the object's paramdp for now*/
- OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->typecode, 0,
OCI_ATTR_TYPECODE, status);
+ OCIAttrGet_parmdp(imp_sth, obj->parmdp, &schema_name, &schema_namel,
OCI_ATTR_SCHEMA_NAME, status);
+
+ if (status != OCI_SUCCESS) {
+ oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
+ return 0;
+ }
+
+ OCIAttrGet_parmdp(imp_sth, obj->parmdp, &type_name, &type_namel,
OCI_ATTR_NAME, status);
+
+ if (status != OCI_SUCCESS) {
+ oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
+ return 0;
+ }
+
+ /* make full type_name: schema_name + "." + type_name */
+ obj->full_type_name = newSVpv((char*)schema_name, schema_namel);
+ sv_catpvn(obj->full_type_name, ".", 1);
+ sv_catpvn(obj->full_type_name, (char*)type_name, type_namel);
+ obj->type_name = (text*)SvPV(obj->full_type_name,na);
/*we need to know its type code*/
+ OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->typecode, 0,
OCI_ATTR_TYPECODE, status);
+
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
+ if (DBIS->debug >= 6 || dbd_verbose >= 6 ) {
+ PerlIO_printf(DBILOGFP, "Geting the properties of object named
=%s at level %d\n",obj->type_name,level);
+ }
+
if (obj->typecode == OCI_TYPECODE_OBJECT){
OCIParam *list_attr= (OCIParam *) 0;
ub2 pos;
@@ -2755,11 +2878,11 @@
fbh->disize =
fbh->disize+long_readlen; /*user set max value for the fetch*/
if (fbh->dbtype == ORA_CLOB){
fbh->ftype = SQLT_CHR;
- }
+ }
else {
fbh->ftype = SQLT_LVB; /*Binary
form seems this is the only value where we cna get the length correctly*/
}
- }
+ }
else if (imp_sth->clbk_lob){ /*get by peice
with callback a slow*/
fbh->clbk_lob = 1;
fbh->define_mode =
OCI_DYNAMIC_FETCH; /* piecwise fetch*/
@@ -2775,7 +2898,7 @@
fbh->ftype = SQLT_BIN; /*other
Binary*/
}
fbh->fetch_func = fetch_clbk_lob;
- }
+ }
else if (imp_sth->piece_lob){ /*get by peice
with polling slowest*/
fbh->piece_lob = 1;
fbh->define_mode =
OCI_DYNAMIC_FETCH; /* piecwise fetch*/
@@ -2792,7 +2915,7 @@
fbh->ftype = SQLT_BIN; /*other
Binary */
}
fbh->fetch_func = fetch_get_piece;
- }
+ }
else { /*auto lob fetch with locator by far the
fastest*/
fbh->disize = fbh->dbsize *10 ; /* XXX!
*/
fbh->fetch_func = (imp_sth->auto_lob) ?
fetch_func_autolob : fetch_func_getrefpv;
@@ -2996,14 +3119,14 @@
for(i=0; i < num_fields; ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i];
- if (fbh->fetch_cleanup)
+ if (fbh->fetch_cleanup)
fbh->fetch_cleanup(sth, fbh);
}
if (ora_fetchtest && DBIc_ROW_COUNT(imp_sth)>0) {
--ora_fetchtest; /* trick for testing performance */
status = OCI_SUCCESS;
- }
+ }
else {
if (DBIS->debug >= 3 || dbd_verbose >= 3 ){
PerlIO_printf(DBILOGFP, " dbd_st_fetch %d
fields...\n", DBIc_NUM_FIELDS(imp_sth));
@@ -3040,7 +3163,7 @@
status=OCI_SUCCESS;
else
status=imp_sth->rs_array_status;
- }
+ }
else {
OCIStmtFetch_log_stat(imp_sth->stmhp,
imp_sth->errhp,1,(ub2)OCI_FETCH_NEXT, OCI_DEFAULT, status);
imp_sth->rs_array_idx=0;
@@ -3081,7 +3204,7 @@
ChopBlanks = DBIc_has(imp_sth, DBIcf_ChopBlanks);
err = 0;
-
+
for(i=0; i < num_fields; ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i];
fb_ary_t *fb_ary = fbh->fb_ary;
@@ -3116,7 +3239,7 @@
if (!fbh->fetch_func(sth, fbh, sv)){
++err; /* fetch_func already called
oci_error */
}
- }
+ }
else {
int datalen =
fb_ary->arlen[imp_sth->rs_array_idx];
char *p = (char*)row_data;
@@ -3125,7 +3248,7 @@
Seems I have to use SQLT_LVB to
get the length all other will fail*/
datalen = *(ub4*)row_data;
sv_setpvn(sv, (char*)row_data+
sizeof(ub4), datalen);
- }
+ }
else {
if (ChopBlanks && fbh->dbtype == 96) {
while(datalen && p[datalen -
1]==' ')
@@ -3138,10 +3261,10 @@
}
}
- }
+ }
else if (rc == 1405) { /* field is null - return undef */
sv_set_undef(sv);
- }
+ }
else { /* See odefin rcode arg description in OCI docs */
char buf[200];
char *hint = "";
@@ -3160,7 +3283,7 @@
hint = ", LongReadLen too small and/or
LongTruncOk not set";
}
- }
+ }
else { /* set field that caused error to undef */
sv_set_undef(sv);
}
@@ -3196,15 +3319,15 @@
if (**uidp == '\0' && **pwdp == '\0') {
return OCI_CRED_EXT;
}
-
+
OCIAttrSet_log_stat(imp_dbh->authp, OCI_HTYPE_SESSION,
*uidp, strlen(*uidp),
(ub4) OCI_ATTR_USERNAME, imp_dbh->errhp, status);
-
+
OCIAttrSet_log_stat(imp_dbh->authp, OCI_HTYPE_SESSION,
(strlen(*pwdp)) ? *pwdp : NULL, strlen(*pwdp),
(ub4) OCI_ATTR_PASSWORD, imp_dbh->errhp, status);
-
+
return OCI_CRED_RDBMS;
}
@@ -3402,7 +3525,7 @@
OCIDescribeAny_log_stat(imp_sth->svchp, errhp, tablename,
strlen(tablename),
(ub1)OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_TABLE,
imp_sth->dschp, status);
-
+
if (status != OCI_SUCCESS) {
/* XXX this OCI_PTYPE_TABLE->OCI_PTYPE_VIEW fallback should actually be
*/
/* a loop that includes synonyms etc */
@@ -3420,12 +3543,12 @@
OCIAttrGet_log_stat(parmhp, OCI_DTYPE_PARAM,
&numcols, 0, OCI_ATTR_NUM_COLS, errhp, status);
}
-
+
if (!status ) {
OCIAttrGet_log_stat(parmhp, OCI_DTYPE_PARAM,
&collisthd, 0, OCI_ATTR_LIST_COLUMNS, errhp,
status);
}
-
+
if (status != OCI_SUCCESS) {
OCIHandleFree_log_stat(imp_sth->dschp, OCI_HTYPE_DESCRIBE,
status);
return oci_error(sth, errhp, status,
"OCIDescribeAny/OCIAttrGet/LOB refetch");
@@ -3446,44 +3569,44 @@
OCI_ATTR_DATA_TYPE,
errhp, status);
if (status)
break;
-
+
OCIAttrGet_log_stat(colhd, OCI_DTYPE_PARAM, &col_name,
&col_name_len,
OCI_ATTR_NAME, errhp, status);
if (status)
break;
-
+
if (DBIS->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(DBILOGFP, " lob refetch
table col %d: '%.*s' otype %d\n",
(int)i, (int)col_name_len,col_name, col_dbtype);
-
+
if (col_dbtype != SQLT_CLOB && col_dbtype != SQLT_BLOB)
continue;
-
+
if (!lob_cols_hv)
lob_cols_hv = newHV();
-
+
sv = newSViv(col_dbtype);
(void)sv_setpvn(sv, col_name, col_name_len);
-
+
if (CSFORM_IMPLIES_UTF8(SQLCS_IMPLICIT))
SvUTF8_on(sv);
-
+
(void)SvIOK_on(sv); /* "what a wonderful hack!" */
(void)hv_store(lob_cols_hv, col_name,col_name_len, sv,0);
OCIDescriptorFree(colhd, OCI_DTYPE_PARAM);
colhd = NULL;
}
-
+
if (colhd)
OCIDescriptorFree(colhd, OCI_DTYPE_PARAM);
-
+
if (status != OCI_SUCCESS) {
oci_error(sth, errhp, status,
"OCIDescribeAny/OCIParamGet/OCIAttrGet/LOB refetch");
OCIHandleFree_log_stat(imp_sth->dschp, OCI_HTYPE_DESCRIBE,
status);
return 0;
}
-
+
if (!lob_cols_hv)
return oci_error(sth, errhp, OCI_ERROR,
"LOB refetch failed, no lobs in table");
@@ -3505,15 +3628,15 @@
while( (sv = hv_iternextsv(imp_sth->all_params_hv, &p, &i)) != NULL ) {
int matched = 0;
phs_t *phs = (phs_t*)(void*)SvPVX(sv);
-
+
if (sv == &sv_undef || !phs)
croak("panic: unbound params");
-
+
if (phs->ftype != SQLT_CLOB && phs->ftype != SQLT_BLOB)
continue;
-
+
hv_iterinit(lob_cols_hv);
-
+
while( (sv = hv_iternextsv(lob_cols_hv, &p, &i)) != NULL ) {
char sql_field[200];
if (phs->ora_field) { /* must match this phs by field
name */
@@ -3572,7 +3695,7 @@
}
}
sv_free((SV*)lob_cols_hv);
-
+
if (unmatched_params) {
Safefree(lr);
return oci_error(sth, errhp, OCI_ERROR,
@@ -3594,7 +3717,7 @@
OCIStmtPrepare_log_stat(lr->stmthp, errhp,
(text*)SvPVX(sql_select), SvCUR(sql_select), OCI_NTV_SYNTAX,
OCI_DEFAULT, status);
-
+
if (status != OCI_SUCCESS) {
OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT);
Safefree(lr);
@@ -3693,12 +3816,12 @@
lr = imp_sth->lob_refetch;
OCIAttrGet_stmhp_stat(imp_sth, lr->rowid, 0, OCI_ATTR_ROWID,status);
-
+
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status, "OCIAttrGet OCI_ATTR_ROWID
/LOB refetch");
-
+
OCIStmtExecute_log_stat(imp_sth->svchp, lr->stmthp, errhp,1, 0, NULL,
NULL, OCI_DEFAULT, status); /* execute and fetch */
-
+
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status,
@@ -3711,7 +3834,7 @@
ub4 amtp;
if(SvUPGRADE(phs->sv, SVt_PV)){/* For GCC not to warn on unused
result */ }; /* just in case */
-
+
amtp = SvCUR(phs->sv); /* XXX UTF8? */
if (rc == 1405) { /* NULL - return undef */
sv_set_undef(phs->sv);
@@ -3747,22 +3870,22 @@
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status,
"OCILobWrite in post_execute_lobs");
}
-
+
} else { /* amtp==0 so truncate LOB to
zero length */
OCILobTrim_log_stat(imp_sth->svchp, errhp,
(OCILobLocator*)fbh->desc_h, 0, status);
-
+
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status,
"OCILobTrim in post_execute_lobs");
}
-
+
}
-
+
if (DBIS->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(DBILOGFP,
" lob refetch %d for '%s' param: ftype
%d, len %ld: %s %s\n",
i+1,fbh->name, fbh->dbtype, ul_t(amtp),
(rc==1405 ? "NULL" : (amtp > 0) ? "LobWrite" :
"LobTrim"), oci_status_name(status));
-
+
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status,
"OCILobTrim/OCILobWrite/LOB refetch");
}
@@ -3784,10 +3907,10 @@
if (lr->rowid)
OCIDescriptorFree(lr->rowid, OCI_DTYPE_ROWID);
OCIHandleFree_log_stat(lr->stmthp, OCI_HTYPE_STMT, status);
-
+
if (status != OCI_SUCCESS)
oci_error(sth, imp_sth->errhp, status,
"ora_free_lob_refetch/OCIHandleFree");
-
+
for(i=0; i < lr->num_fields; ++i) {
imp_fbh_t *fbh = &lr->fbh_ary[i];
ora_free_fbh_contents(fbh);
Modified: dbd-oracle/trunk/ocitrace.h
==============================================================================
--- dbd-oracle/trunk/ocitrace.h (original)
+++ dbd-oracle/trunk/ocitrace.h Wed Jan 14 07:28:59 2009
@@ -191,6 +191,14 @@
OciTp, (void*)envhp, (void*)errhp, (void*)svchp,
(char*)(p1),(l),oci_status_name(stat)),stat \
: stat
+#define OCITypeByRef_log_stat(envhp,errhp,ref,tdo,stat)\
+ stat =
OCITypeByRef(envhp,errhp,ref,OCI_DURATION_TRANS,OCI_TYPEGET_ALL,tdo);\
+ (DBD_OCI_TRACEON) \
+ ? PerlIO_printf(DBD_OCI_TRACEFP,\
+ "%sTypeByRef(%p,%p,%p)=%s\n",\
+ OciTp, (void*)envhp, (void*)errhp,
(void*)ref,oci_status_name(stat)),stat \
+ : stat
+
/* added by lab */
#define OCILobCharSetId_log_stat( envhp, errhp, locp, csidp, stat ) \
stat = OCILobCharSetId( envhp, errhp, locp, csidp ); \
@@ -343,7 +351,7 @@
#define OCIInitialize_log_stat(md,cp,mlf,rlf,mfp,stat)
\
stat=OCIInitialize(md,cp,mlf,rlf,mfp); \
(DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,
\
- "%sInitialize(%lu,with mode =%s %p,%p,%p,%p)=%s\n",
\
+ "%sInitialize(with mode =%s %lu,%p,%p,%p,%p)=%s\n",
\
OciTp,
oci_mode(md),ul_t(md),(void*)cp,(void*)mlf,(void*)rlf,(void*)mfp, \
oci_status_name(stat)),stat : stat
Added: dbd-oracle/trunk/t/58object.t
==============================================================================
--- (empty file)
+++ dbd-oracle/trunk/t/58object.t Wed Jan 14 07:28:59 2009
@@ -0,0 +1,153 @@
+#!perl -w
+
+use DBI;
+use DBD::Oracle qw(ORA_RSET SQLCS_NCHAR);
+use strict;
+use Data::Dumper;
+
+use Test::More tests => 34;
+unshift @INC ,'t';
+require 'nchar_test_lib.pl';
+
+$| = 1;
+
+BEGIN {
+ use_ok('DBI');
+}
+
+$ENV{NLS_DATE_FORMAT} = 'YYYY-MM-DD"T"HH24:MI:SS';
+
+# create a database handle
+my $dsn = oracle_test_dsn();
+my $dbuser = $ENV{ORACLE_USERID} || 'scott/tiger';
+my $dbh = DBI->connect($dsn, $dbuser, '',{ RaiseError=>1,
+ AutoCommit=>1,
+ PrintError => 0,
+ ora_objects => 1 });
+
+my ($schema) = $dbuser =~ m{^([^/]*)};
+
+# Test ora_objects flag
+cmp_ok($dbh->{ora_objects}, 'eq', '1', 'ora_objects flag is set to 1');
+
+$dbh->{ora_objects} = 0;
+cmp_ok($dbh->{ora_objects}, 'eq', '0', 'ora_objects flag is set to 0');
+
+# check that our db handle is good
+isa_ok($dbh, "DBI::db");
+
+my $obj_prefix = "dbd_test_";
+my $super_type = "${obj_prefix}_type_A";
+my $sub_type = "${obj_prefix}_type_B";
+my $table = "${obj_prefix}_obj_table";
+
+sub drop_test_objects {
+ for my $obj ("TABLE $table", "TYPE $sub_type", "TYPE $super_type") {
+ #do not warn if already there
+ eval {
+ local $dbh->{PrintError} = 0;
+ $dbh->do(qq{drop $obj});
+ };
+ }
+}
+
+&drop_test_objects;
+
+$dbh->do(qq{ CREATE OR REPLACE TYPE $super_type AS OBJECT (
+ num INTEGER,
+ name VARCHAR2(20)
+ ) NOT FINAL }) or die $dbh->errstr;
+
+$dbh->do(qq{ CREATE OR REPLACE TYPE $sub_type UNDER $super_type (
+ datetime DATE,
+ amount NUMERIC(10,5)
+ ) NOT FINAL }) or die $dbh->errstr;
+$dbh->do(qq{ CREATE TABLE $table (id INTEGER, obj $super_type) })
+ or die $dbh->errstr;
+$dbh->do(qq{ INSERT INTO $table VALUES (1, $super_type(13, 'obj1')) })
+ or die $dbh->errstr;
+$dbh->do(qq{ INSERT INTO $table VALUES (2, $sub_type(NULL, 'obj2',
+ TO_DATE('2004-11-30 14:27:18', 'YYYY-MM-DD HH24:MI:SS'),
+ 12345.6789)) }
+ ) or die $dbh->errstr;
+$dbh->do(qq{ INSERT INTO $table VALUES (3, $sub_type(5, 'obj3', NULL,
777.666)) }
+ ) or die $dbh->errstr;
+
+# Test old (backward compatible) interface
+
+# test select testing objects
+my $sth = $dbh->prepare("select * from $table order by id");
+ok ($sth, 'old: Prepare select');
+ok ($sth->execute(), 'old: Execute select');
+
+my @row1 = $sth->fetchrow();
+ok (scalar @row1, 'old: Fetch first row');
+cmp_ok(ref $row1[1], 'eq', 'ARRAY', 'old: Row 1 column 2 is an ARRAY');
+cmp_ok(scalar(@{$row1[1]}), '==', 2, 'old: Row 1 column 2 is has 2 elements');
+
+my @row2 = $sth->fetchrow();
+ok (scalar @row2, 'old: Fetch second row');
+cmp_ok(ref $row2[1], 'eq', 'ARRAY', 'old: Row 2 column 2 is an ARRAY');
+cmp_ok(scalar(@{$row2[1]}), '==', 2, 'old: Row 2 column 2 is has 2 elements');
+
+my @row3 = $sth->fetchrow();
+ok (scalar @row3, 'old: Fetch third row');
+cmp_ok(ref $row3[1], 'eq', 'ARRAY', 'old: Row 3 column 2 is an ARRAY');
+cmp_ok(scalar(@{$row3[1]}), '==', 2, 'old: Row 3 column 2 is has 2 elements');
+
+ok (!$sth->fetchrow(), 'old: No more rows expected');
+
+#print STDERR Dumper(\...@row1, \...@row2, \...@row3);
+
+# Test new (extended) object interface
+
+# enable extended object support
+$dbh->{ora_objects} = 1;
+
+# test select testing objects - in extended mode
+$sth = $dbh->prepare("select * from $table order by id");
+ok ($sth, 'new: Prepare select');
+ok ($sth->execute(), 'new: Execute select');
+
+
+...@row1 = $sth->fetchrow();
+ok (scalar @row1, 'new: Fetch first row');
+cmp_ok(ref $row1[1], 'eq', 'DBD::Oracle::Object', 'new: Row 1 column 2 is an
DBD:Oracle::Object');
+cmp_ok(uc $row1[1]->type_name, "eq", uc "$schema.$super_type", "new: Row 1
column 2 object type");
+is_deeply([$row1[1]->attributes], ['NUM', 13, 'NAME', 'obj1'], "new: Row 1
column 2 object attributes");
+
+...@row2 = $sth->fetchrow();
+ok (scalar @row2, 'new: Fetch second row');
+cmp_ok(ref $row2[1], 'eq', 'DBD::Oracle::Object', 'new: Row 2 column 2 is an
DBD::Oracle::Object');
+cmp_ok(uc $row2[1]->type_name, "eq", uc "$schema.$sub_type", "new: Row 2
column 2 object type");
+is_deeply([$row2[1]->attributes], ['NUM', undef, 'NAME', 'obj2',
+ 'DATETIME', '2004-11-30T14:27:18', 'AMOUNT', '12345.6789'], "new:
Row 1 column 2 object attributes");
+
+...@row3 = $sth->fetchrow();
+ok (scalar @row3, 'new: Fetch third row');
+cmp_ok(ref $row3[1], 'eq', 'DBD::Oracle::Object', 'new: Row 3 column 2 is an
DBD::Oracle::Object');
+cmp_ok(uc $row3[1]->type_name, "eq", uc "$schema.$sub_type", "new: Row 3
column 2 object type");
+is_deeply([$row3[1]->attributes], ['NUM', 5, 'NAME', 'obj3',
+ 'DATETIME', undef, 'AMOUNT', '777.666'], "new: Row 1 column 2
object attributes");
+
+ok (!$sth->fetchrow(), 'new: No more rows expected');
+
+#print STDERR Dumper(\...@row1, \...@row2, \...@row3);
+
+# Test DBD::Oracle::Object
+my $obj = $row3[1];
+my $expected_hash = {
+ NUM => 5,
+ NAME => 'obj3',
+ DATETIME => undef,
+ AMOUNT => 777.666,
+ };
+is_deeply($obj->attr_hash, $expected_hash, 'DBD::Oracle::Object->attr_hash');
+is_deeply($obj->attr, $expected_hash, 'DBD::Oracle::Object->attr');
+is($obj->attr("NAME"), 'obj3', 'DBD::Oracle::Object->attr("NAME")');
+
+#cleanup
+&drop_test_objects;
+$dbh->disconnect;
+
+1;