Hello!

        A preliminary version of OCI array bind patch attached.
Apply to the latest trunk release.
        Usage example also attached.

1) Only Varchar2 tables are supported. ( SYS.DBMS_SQL.VARCHAR2_TABLE )
2) utf8 is untested. Seems to currupt utf8 data on truncate.
3) Untested.

        I plan to support bind of SYS.DBMS_SQL.NUMBER_TABLE also.


        As I'm new to perl api, your comments are welcome.

        Also, I cannot test the whole functionality much. So, bug
reports are welcome.

                Bye. Alex.

#!/usr/bin/perl

use strict;
use warnings;

BEGIN{
        use lib 'blib/lib';
        use lib 'blib/arch';
        use lib 'blib/arch/auto';
        use lib 'lib';
        use lib '.';
};

use Data::Dumper;
use DBI;


use DBD::Oracle qw(:ora_types);

my $dsn = 'dbi:Oracle:mydb';
my $user = 'myuser';
my $password = 'mypw';

my $dbh = DBI->connect($dsn, $user, $password,
                        { RaiseError => 0, AutoCommit => 0 });

my $statement='
DECLARE
        tbl     SYS.DBMS_SQL.VARCHAR2_TABLE;
BEGIN
        tbl := :mytable;
        :cc := tbl.count();
        tbl(1) := \'def\';
        tbl(2) := \'ijk\';
        :mytable := tbl;
END;
';

my $sth=$dbh->prepare( $statement );

if( ! defined($sth) ){
        die "Prapare error: ",$dbh->errstr,"\n";
}

my @arr=( "abc" );

if( not $sth->bind_param_inout(":mytable", [EMAIL PROTECTED], 10, { TYPE => 
DBD::Oracle::ORA_VARCHAR2, ora_maxarray_numentries => 100 } ) ){
        die "bind :mytable error: ",$dbh->errstr,"\n";
}
my $cc;
if( not $sth->bind_param_inout(":cc", \$cc, 100 ) ){
        die "bind :cc error: ",$dbh->errstr,"\n";
}

if( not $sth->execute() ){
        die "Execute failed: ",$dbh->errstr,"\n";
}
print   "Result: cc=",$cc,"\n",
        "\tarr=",Data::Dumper::Dumper([EMAIL PROTECTED]),"\n";

$dbh->disconnect();
Index: ocitrace.h
===================================================================
--- ocitrace.h  (revision 9916)
+++ ocitrace.h  (working copy)
@@ -98,12 +98,20 @@
 #define OCIBindByName_log_stat(sh,bp,eh,p1,pl,v,vs,dt,in,al,rc,mx,cu,md,stat)  
\
        stat=OCIBindByName(sh,bp,eh,p1,pl,v,vs,dt,in,al,rc,mx,cu,md);   \
        (DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,                      
\
-         
"%sBindByName(%p,%p,%p,\"%s\",%ld,%p,%ld,%u,%p,%p,%p,%lu,%p,%lu)=%s\n",\
+         
"%sBindByName(%p,%p,%p,\"%s\",placeh_len=%ld,value_p=%p,value_sz=%ld," \
+         "dty=%u,indp=%p,alenp=%p,rcodep=%p,maxarr_len=%lu,curelep=%p 
(*=%d),mode=%lu)=%s\n",\
          OciTp, (void*)sh,(void*)bp,(void*)eh,p1,sl_t(pl),(void*)(v),  \
          sl_t(vs),(ub2)(dt),(void*)(in),(ub2*)(al),(ub2*)(rc),         \
-         ul_t((mx)),pul_t((cu)),ul_t((md)),                            \
+         ul_t((mx)),pul_t((cu)),(cu ? *(int*)cu : 0 ) ,ul_t((md)),             
                \
          oci_status_name(stat)),stat : stat
 
+#define        OCIBindArrayOfStruct_log_stat(bp,ep,sd,si,sl,sr,stat)   \
+       stat=OCIBindArrayOfStruct(bp,ep,sd,si,sl,sr);           \
+       (DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,      \
+         "%sOCIBindArrayOfStruct(%p,%p,%u,%u,%u,%u)=%s\n",     \
+         OciTp,(void*)bp,(void*)ep,sd,si,sl,sr,                \
+         oci_status_name(stat)),stat : stat
+
 #define OCIBindDynamic_log(bh,eh,icx,cbi,ocx,cbo,stat)                 \
        stat=OCIBindDynamic(bh,eh,icx,cbi,ocx,cbo);                     \
        (DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,                      
\
Index: dbdimp.c
===================================================================
--- dbdimp.c    (revision 9916)
+++ dbdimp.c    (working copy)
@@ -1141,8 +1141,437 @@
     }
 }
 
+/* ############### Array bind ######################################### */
+/* 
+ *
+ * Realloc temporary array buffer to match required number of entries
+ * and buffer size.
+ *
+ * Return value: croaks on error. false (=0 ) on success.
+ * */
+int ora_realloc_phs_array(phs_t *phs,int newentries, int newbufsize){
+    int i; /* Loop variable */
+    if( newbufsize < 0 ){
+       newbufsize=0;
+    }
+    if( newentries > phs->array_numallocated ){
+       OCIInd *newind=(OCIInd 
*)realloc(phs->array_indicators,newentries*sizeof(OCIInd) );
+       if( newind ){
+           phs->array_indicators=newind;
+           /* Init all indicators to NULL values. */
+           for( i=phs->array_numallocated; i < newentries ; i++ ){
+               newind[i]=1;
+           }
+       }else{
+           croak("Not enough memory to allocate %d OCI 
indicators.",newentries);
+       }
+       unsigned short *newal=(unsigned short *)realloc(
+               phs->array_lengths,
+               newentries*sizeof(unsigned short)
+       );
+       if( newal ){
+           phs->array_lengths=newal;
+           /* Init all new lengths to zero */
+           if( newentries > phs->array_numallocated ){
+                   memset(
+                           &(newal[phs->array_numallocated]),
+                           0,
+                           
(newentries-(phs->array_numallocated))*sizeof(unsigned short)
+                         );
+           }
+       }else{
+           croak("Not enough memory to allocate %d entries in OCI array of 
lengths.",newentries);
+       }
+       phs->array_numallocated=newentries;
+    }
+    if( phs->array_buflen < newbufsize ){
+       char * newbuf=(char *)realloc( phs->array_buf, (unsigned) newbufsize );
+       if( newbuf ){
+           phs->array_buf=newbuf;
+       }else{
+           croak("Not enough memory to allocate OCI array buffer of %d 
bytes.",newbufsize);
+       }
+       phs->array_buflen=newbufsize;
+    }
+    return 0;
+}
+/* bind of SYS.DBMS_SQL.VARCHAR2_TABLE */
+int
+dbd_rebind_ph_varchar2_table(SV *sth, imp_sth_t *imp_sth, phs_t *phs)
+{
+       dTHX;
+       /*D_imp_dbh_from_sth ;*/
+    sword status;
+    int trace_level = DBIS->debug;
+    AV *arr;
+    ub1 csform;
+    ub2 csid;
+    int flag_data_is_utf8=0;
+    
+    if( ( ! SvROK(phs->sv) )  || (SvTYPE(SvRV(phs->sv))!=SVt_PVAV) ) { /* 
Allow only array binds */
+       croak("dbd_rebind_ph_varchar2_table(): bad bind variable. ARRAY 
reference required, but got %s for '%s'.",
+                   neatsvpv(phs->sv,0), phs->name);
+    }
+    arr=(AV*)(SvRV(phs->sv));
 
+    if (trace_level >= 2){
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): 
array_numstruct=%d\n",
+             phs->array_numstruct);
+    }
+    /* If no number of entries to bind specified,
+     * set phs->array_numstruct to the scalar(@array) bound.
+     */
+    if( phs->array_numstruct <= 0 ){
+       /* av_len() returns last array index, or -1 is array is empty */
+       int numarrayentries=av_len( arr );
+       if( numarrayentries >= 0 ){
+           phs->array_numstruct = numarrayentries+1;
+           if (trace_level >= 2){
+               PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): 
array_numstruct=%d (calculated) \n",
+                       phs->array_numstruct);
+           }
+       }
+    }
+    /* Fix charset */
+    csform = phs->csform;
+    if (trace_level >= 2){
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): original 
csform=%d\n",
+             (int)csform);
+    }
+    /* Calculate each bound structure maxlen.
+     * If maxlen<=0, let maxlen=MAX ( length($$_) each @array );
+     *
+     * Charset calculation is done inside this loop either.
+     */
+    {
+       int maxlen=0;
+       int i;
+       for(i=0;i<av_len(arr)+1;i++){
+           SV *item;
+           item=*(av_fetch(arr,i,0));
+           if( item ){
+               if( phs->maxlen <=0 ){ /* Analyze maxlength only if not forced 
*/
+                   STRLEN length=0;
+                   if (!SvPOK(item)) {     /* normalizations for special cases 
    */
+                       if (SvOK(item)) {    /* ie a number, convert to string 
ASAP  */
+                           if (!(SvROK(item) && phs->is_inout)){
+                               sv_2pv(item, &length);
+                           }
+                       } else { /* ensure we're at least an SVt_PV (so SvPVX 
etc work)     */
+                           SvUPGRADE(item, SVt_PV);
+                       }
+                   }
+                   if( length == 0 ){
+                       length=SvCUR(item);
+                   }
+                   if( length+1 > maxlen ){
+                       maxlen=length+1;
+                   }
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): length(array[%d])=%d\n",
+                               i,(int)length);
+                   }
+               }
+               if(SvUTF8(item) ){
+                   flag_data_is_utf8=1;
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): is_utf8(array[%d])=true\n", i);
+                   }
+                   if (csform != SQLCS_NCHAR) {
+                       /* try to default csform to avoid translation through 
non-unicode */
+                       if (CSFORM_IMPLIES_UTF8(SQLCS_NCHAR))           /* 
prefer NCHAR */
+                           csform = SQLCS_NCHAR;
+                       else if (CSFORM_IMPLIES_UTF8(SQLCS_IMPLICIT))
+                           csform = SQLCS_IMPLICIT;
+                       /* else leave csform == 0 */
+                       if (trace_level)
+                           PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): rebinding %s with UTF8 value %s", phs->name,
+                                   (csform == SQLCS_NCHAR)    ? "so setting 
csform=SQLCS_IMPLICIT" :
+                                   (csform == SQLCS_IMPLICIT) ? "so setting 
csform=SQLCS_NCHAR" :
+                                   "but neither CHAR nor NCHAR are unicode\n");
+                   }
+               }else{
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): is_utf8(array[%d])=false\n", i);
+                   }
+               }
+           }
+       }
+       if( phs->maxlen <=0 ){
+           phs->maxlen=maxlen;
+           if (trace_level >= 2){
+               PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): 
phs->maxlen calculated  =%d\n",
+                       (int)maxlen);
+           }
+       } else{
+           if (trace_level >= 2){
+               PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): 
phs->maxlen forsed =%d\n",
+                       (int)maxlen);
+           }
+       }
+    }
+    /* Do not allow string bind longer than max VARCHAR2=4000+1 */
+    if( phs->maxlen > 4001 ){
+       phs->maxlen=4001;
+    }
 
+    if( phs->array_numstruct == 0 ){
+       /* Oracle doesn't allow NULL buffers even for empty tables. Don't know 
why. */
+       phs->array_numstruct=1;
+    }
+    int need_allocate_rows=phs->ora_maxarray_numentries;
+    
+    if( need_allocate_rows< phs->array_numstruct ){
+       need_allocate_rows=phs->array_numstruct;
+    }
+    int buflen=need_allocate_rows* phs->maxlen; /* We need buffer for at least 
ora_maxarray_numentries entries */
+    /* Upgrade array buffer to new length */
+    if( ora_realloc_phs_array(phs,need_allocate_rows,buflen) ){
+       croak("Unable to bind %s - %d structures by %d bytes requires too much 
memory.",
+               phs->name, need_allocate_rows, buflen );
+    }
+    if (trace_level >= 2){
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): "
+               "Call ora_realloc_phs_array(, array_numstruct=%d, buflen=%d) 
successfull.\n",
+               phs->array_numstruct,buflen);
+    }
+    /* Fill array buffer with string data */
+
+    {
+       int i; /* Not to require C99 mode */
+       for(i=0;i<av_len(arr)+1;i++){
+           SV *item;
+           item=*(av_fetch(arr,i,0));
+           if( item ){
+               STRLEN itemlen;
+               char *str=SvPV(item, itemlen);
+               if( str && (itemlen>0) ){
+                   /* Limit string length to maxlen. FIXME: This may corrupt 
UTF-8 data. */
+                   if( itemlen > phs->maxlen-1 ){
+                       itemlen=phs->maxlen-1;
+                   }
+                   memcpy( phs->array_buf+phs->maxlen*i,
+                           str,
+                           itemlen);
+                   /* Set last byte to zero */
+                   phs->array_buf[ phs->maxlen*i + itemlen ]=0;
+                   phs->array_indicators[i]=0;
+                   phs->array_lengths[i]=itemlen+1; /* Zero byte */
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): "
+                               "Copying length=%d array[%d]='%s'.\n",
+                               itemlen,i,str);
+                   }
+               }else{
+                   /* Mark NULL */
+                   phs->array_indicators[i]=1;
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP, 
"dbd_rebind_ph_varchar2_table(): "
+                               "Copying length=%d array[%d]=NULL (length==0 or 
! str) .\n",
+                               itemlen,i);
+                   }
+               }
+           }else{
+               /* Mark NULL */
+               phs->array_indicators[i]=1;
+               if (trace_level >= 3){
+                   PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): "
+                           "Copying length=? array[%d]=NULL av_fetch 
failed.\n", i);
+               }
+           }
+       }
+    }
+    /* Do actual bind */
+    OCIBindByName_log_stat(imp_sth->stmhp, &phs->bndhp, imp_sth->errhp,
+           (text*)phs->name, (sb4)strlen(phs->name),
+           phs->array_buf,
+           phs->maxlen,
+           (ub2)SQLT_STR, phs->array_indicators,
+           phs->array_lengths, /* ub2 *alen_ptr not needed with OCIBindDynamic 
*/
+           (ub2)0,
+           (ub4)phs->ora_maxarray_numentries, /* max elements that can fit in 
allocated array  */
+           &(phs->array_numstruct),    /* (ptr to) current number of elements 
in array */
+           OCI_DEFAULT,                /* OCI_DATA_AT_EXEC (bind with 
callbacks) or OCI_DEFAULT  */
+           status
+    );
+    if (status != OCI_SUCCESS) {
+       oci_error(sth, imp_sth->errhp, status, "OCIBindByName");
+       return 0;
+    }
+    OCIBindArrayOfStruct_log_stat(phs->bndhp, imp_sth->errhp,
+           phs->maxlen,            /* Skip parameter for the next data value */
+           sizeof (OCIInd),        /* Skip parameter for the next indicator 
value */
+           sizeof(unsigned short), /* Skip parameter for the next actual 
length value */
+           0,                      /* Skip parameter for the next column-level 
error code */
+           status);
+    if (status != OCI_SUCCESS) {
+       oci_error(sth, imp_sth->errhp, status, "OCIBindArrayOfStruct");
+       return 0;
+    }
+    /* Fixup charset */
+    if (csform) {
+       /* set OCI_ATTR_CHARSET_FORM before we get the default 
OCI_ATTR_CHARSET_ID */
+       OCIAttrSet_log_stat(phs->bndhp, (ub4) OCI_HTYPE_BIND,
+           &csform, (ub4) 0, (ub4) OCI_ATTR_CHARSET_FORM, imp_sth->errhp, 
status);
+       if ( status != OCI_SUCCESS ) {
+           oci_error(sth, imp_sth->errhp, status, 
ora_sql_error(imp_sth,"OCIAttrSet (OCI_ATTR_CHARSET_FORM)"));
+           return 0;
+       }
+    }
+
+    if (!phs->csid_orig) {     /* get the default csid Oracle would use */
+       OCIAttrGet_log_stat(phs->bndhp, OCI_HTYPE_BIND, &phs->csid_orig, (ub4)0 
,
+               OCI_ATTR_CHARSET_ID, imp_sth->errhp, status);
+    }
+
+    /* if app has specified a csid then use that, else use default */
+    csid = (phs->csid) ? phs->csid : phs->csid_orig;
+
+    /* if data is utf8 but charset isn't then switch to utf8 csid */
+    if ( flag_data_is_utf8 && !CS_IS_UTF8(csid))
+        csid = utf8_csid; /* not al32utf8_csid here on purpose */
+
+    if (trace_level >= 3)
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_varchar2_table(): bind %s <== %s 
"
+               "(%s, %s, csid %d->%d->%d, ftype %d, csform %d->%d, maxlen %lu, 
maxdata_size %lu)\n",
+             phs->name, neatsvpv(phs->sv,0),
+             (phs->is_inout) ? "inout" : "in",
+             flag_data_is_utf8 ? "is-utf8" : "not-utf8",
+             phs->csid_orig, phs->csid, csid,
+             phs->ftype, phs->csform, csform,
+             (unsigned long)phs->maxlen, (unsigned long)phs->maxdata_size);
+
+
+    if (csid) {
+       OCIAttrSet_log_stat(phs->bndhp, (ub4) OCI_HTYPE_BIND,
+           &csid, (ub4) 0, (ub4) OCI_ATTR_CHARSET_ID, imp_sth->errhp, status);
+       if ( status != OCI_SUCCESS ) {
+           oci_error(sth, imp_sth->errhp, status, 
ora_sql_error(imp_sth,"OCIAttrSet (OCI_ATTR_CHARSET_ID)"));
+           return 0;
+       }
+    }
+
+    if (phs->maxdata_size) {
+       OCIAttrSet_log_stat(phs->bndhp, (ub4)OCI_HTYPE_BIND,
+           phs->array_buf, (ub4)phs->array_buflen, (ub4)OCI_ATTR_MAXDATA_SIZE, 
imp_sth->errhp, status);
+       if ( status != OCI_SUCCESS ) {
+           oci_error(sth, imp_sth->errhp, status, 
ora_sql_error(imp_sth,"OCIAttrSet (OCI_ATTR_MAXDATA_SIZE)"));
+           return 0;
+       }
+    }
+
+    return 2;
+}
+
+
+/* Copy array data from array buffer into perl array */
+/* Returns false on error, true on success */
+int dbd_phs_array_fixup_after_execute(phs_t *phs){
+       dTHX;
+
+    int trace_level = DBIS->debug;
+    AV *arr;
+
+    if( ( ! SvROK(phs->sv) )  || (SvTYPE(SvRV(phs->sv))!=SVt_PVAV) ) { /* 
Allow only array binds */
+       croak("dbd_phs_array_fixup_after_execute(): bad bind variable. ARRAY 
reference required, but got %s for '%s'.",
+                   neatsvpv(phs->sv,0), phs->name);
+    }
+    if (trace_level >= 1){
+       PerlIO_printf(DBILOGFP,
+               "dbd_phs_array_fixup_after_execute(): Called for '%s' : 
array_numstruct=%d, maxlen=%d \n",
+               phs->name,
+               phs->array_numstruct,
+               phs->maxlen
+               );
+    }
+    arr=(AV*)(SvRV(phs->sv));
+
+    /* If no data is returned, just clear the array. */
+    if( phs->array_numstruct <= 0 ){
+       av_clear(arr);
+       return 1;
+    }
+    /* Delete extra data from array, if any */
+    while( av_len(arr) >= phs->array_numstruct ){
+       av_delete(arr,av_len(arr),G_DISCARD);
+    };
+    /* Extend array, if needed. */
+    if( av_len(arr)+1 < phs->array_numstruct ){
+       av_extend(arr,phs->array_numstruct-1);
+    } 
+    /* Fill array with buffer data */
+    {
+       /* phs_t */
+       int i; /* Not to require C99 mode */
+       for(i=0;i<phs->array_numstruct;i++){
+           SV *item,**pitem;
+           pitem=av_fetch(arr,i,0);
+           if( pitem ){
+               item=*pitem;
+           }else{
+               item=NULL;
+           }
+           if( phs->array_indicators[i] == -1 ){
+               /* NULL */
+               if( item ){
+                   SvSetMagicSV(item,&PL_sv_undef);
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP,
+                               "dbd_phs_array_fixup_after_execute(): arr[%d] = 
undef; SvSetMagicSV(item,&PL_sv_undef);\n",
+                               i
+                               );
+                   }
+               }else{
+                   av_store(arr,i,&PL_sv_undef);
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP,
+                               "dbd_phs_array_fixup_after_execute(): arr[%d] = 
undef; av_store(arr,i,&PL_sv_undef);\n",
+                               i
+                               );
+                   }
+               }
+           }else{
+               if( (phs->array_indicators[i] == -2) || 
(phs->array_indicators[i] > 0) ){
+                   /* Truncation occurred */
+                   if (trace_level >= 2){
+                       PerlIO_printf(DBILOGFP,
+                               "dbd_phs_array_fixup_after_execute(): 
Placeholder '%s': data truncated at %d row.\n",
+                               phs->name,i);
+                   }
+               }else{
+                   /* All OK. Just copy value.*/
+               }
+               if( item ){
+                   
sv_setpvn_mg(item,phs->array_buf+phs->maxlen*i,phs->array_lengths[i]);
+                   SvPOK_only_UTF8(item);
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP,
+                               "dbd_phs_array_fixup_after_execute(): arr[%d] = 
'%s'; "
+                                       
"sv_setpvn_mg(item,phs->array_buf+phs->maxlen*i,phs->array_lengths[i]); \n",
+                                       i, phs->array_buf+phs->maxlen*i
+                               );
+                   }
+               }else{
+                   
av_store(arr,i,newSVpvn(phs->array_buf+phs->maxlen*i,phs->array_lengths[i]));
+                   if (trace_level >= 3){
+                       PerlIO_printf(DBILOGFP,
+                               "dbd_phs_array_fixup_after_execute(): arr[%d] = 
'%s'; "
+                                       
"av_store(arr,i,newSVpvn(phs->array_buf+phs->maxlen*i,phs->array_lengths[i])); 
\n",
+                                       i, phs->array_buf+phs->maxlen*i
+                               );
+                   }
+               }
+           }
+       }
+    }
+    if (trace_level >= 2){
+       PerlIO_printf(DBILOGFP,
+               "dbd_phs_array_fixup_after_execute(): scalar(@arr)=%d.\n",
+               av_len(arr)+1);
+    }
+    return 1;
+}
+
 static int
 dbd_rebind_ph_char(SV *sth, imp_sth_t *imp_sth, phs_t *phs, ub2 **alen_ptr_ptr)
 {
@@ -1162,7 +1591,7 @@
 
     if (DBIS->debug >= 2) {
        char *val = neatsvpv(phs->sv,0);
-       PerlIO_printf(DBILOGFP, "       bind %s <== %.1000s (", phs->name, val);
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_char() (1): bind %s <== %.1000s 
(", phs->name, val);
        if (!SvOK(phs->sv))
            PerlIO_printf(DBILOGFP, "NULL, ");
        PerlIO_printf(DBILOGFP, "size %ld/%ld/%ld, ",
@@ -1217,7 +1646,7 @@
 
     if (DBIS->debug >= 3) {
        UV neatsvpvlen = (UV)DBIc_DBISTATE(imp_sth)->neatsvpvlen;
-       PerlIO_printf(DBILOGFP, "       bind %s <== '%.*s' (size %ld/%ld, otype 
%d, indp %d, at_exec %d)\n",
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph_char() (2): bind %s <== '%.*s' 
(size %ld/%ld, otype %d, indp %d, at_exec %d)\n",
            phs->name,
            (int)(phs->alen > neatsvpvlen ? neatsvpvlen : phs->alen),
            (phs->progv) ? phs->progv : "",
@@ -1227,7 +1656,6 @@
     return 1;
 }
 
-
 /*
  * Rebind an "in" cursor ref to its real statement handle
  * This allows passing cursor refs as "in" to pl/sql (but only if you got the
@@ -1371,10 +1799,34 @@
     ub2 csid;
 
     if (trace_level >= 5)
-       PerlIO_printf(DBILOGFP, "       rebinding %s (%s, ftype %d, csid %d, 
csform %d, inout %d)\n",
-               phs->name, (SvUTF8(phs->sv) ? "is-utf8" : "not-utf8"),
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph() (1): rebinding %s as %s (%s, 
ftype %d, csid %d, csform %d, inout %d)\n",
+               phs->name, (SvPOK(phs->sv) ? neatspv(phs->sv,0) : 
"NULL"),(SvUTF8(phs->sv) ? "is-utf8" : "not-utf8"),
                phs->ftype, phs->csid, phs->csform, phs->is_inout);
 
+    if( SvROK(phs->sv) ){
+       if (SvTYPE(SvRV(phs->sv))!=SVt_PVAV) { /* Allow only array binds */
+           croak("Reference bind allowed only for ARRAY references, but got %s 
for '%s'",
+                   neatsvpv(phs->sv,0), phs->name);
+       }
+       if(     (phs->ftype == SQLT_CLOB) ||
+               (phs->ftype == SQLT_BLOB) ||
+               (phs->ftype == SQLT_RSET) ){
+
+           croak("Array binding is not allowed for CLOB/BLOB/RSET datatype for 
'%s'",
+                   phs->name);
+       }
+       switch( phs->ftype ){
+       default:
+           done = dbd_rebind_ph_varchar2_table(sth, imp_sth, phs);
+       }
+       if( done == 2 ){ /* the dbd_rebind_* did the OCI bind call itself 
successfully */
+           if (trace_level >= 3)
+               PerlIO_printf(DBILOGFP, "       bind %s done with ftype %d\n",
+                   phs->name, phs->ftype);
+           return 1;
+       }
+       return 0; /* the rebind failed   */
+    }
     switch (phs->ftype) {
     case SQLT_CLOB:
     case SQLT_BLOB:
@@ -1435,7 +1887,7 @@
            csform = SQLCS_IMPLICIT;
        /* else leave csform == 0 */
        if (trace_level)
-           PerlIO_printf(DBILOGFP, "       rebinding %s with UTF8 value %s", 
phs->name,
+           PerlIO_printf(DBILOGFP, "dbd_rebind_ph() (2): rebinding %s with 
UTF8 value %s", phs->name,
                (csform == SQLCS_NCHAR)    ? "so setting csform=SQLCS_IMPLICIT" 
:
                (csform == SQLCS_IMPLICIT) ? "so setting csform=SQLCS_NCHAR" :
                    "but neither CHAR nor NCHAR are unicode\n");
@@ -1464,7 +1916,7 @@
         csid = utf8_csid; /* not al32utf8_csid here on purpose */
 
     if (trace_level >= 3)
-       PerlIO_printf(DBILOGFP, "       bind %s <== %s "
+       PerlIO_printf(DBILOGFP, "dbd_rebind_ph(): bind %s <== %s "
                "(%s, %s, csid %d->%d->%d, ftype %d, csform %d->%d, maxlen %lu, 
maxdata_size %lu)\n",
              phs->name, neatsvpv(phs->sv,0),
              (phs->is_inout) ? "inout" : "in",
@@ -1529,16 +1981,18 @@
     if (SvROK(newvalue)
        && !IS_DBI_HANDLE(newvalue)     /* dbi handle allowed for cursor 
variables */
        && !SvAMAGIC(newvalue)          /* overload magic allowed (untested) */
-    && !sv_derived_from(newvalue, "OCILobLocatorPtr" )  /* input LOB locator*/
+       && !sv_derived_from(newvalue, "OCILobLocatorPtr" )  /* input LOB 
locator*/
+       && !(SvTYPE(SvRV(newvalue))==SVt_PVAV) /* Allow array binds */
     )
        croak("Can't bind a reference (%s)", neatsvpv(newvalue,0));
-    if (SvTYPE(newvalue) > SVt_PVLV) /* hook for later array logic?    */
-       croak("Can't bind a non-scalar value (%s)", neatsvpv(newvalue,0));
+    /*if (SvTYPE(newvalue) > SVt_PVLV)*/ /* hook for later array logic?        
*/
+    if (SvTYPE(newvalue) > SVt_PVAV) /* Array binding supported */
+       croak("Can't bind a non-scalar, non-array value (%s)", 
neatsvpv(newvalue,0));
     if (SvTYPE(newvalue) == SVt_PVLV && is_inout)      /* may allow later */
        croak("Can't bind ``lvalue'' mode scalar as inout parameter 
(currently)");
 
     if (DBIS->debug >= 2) {
-       PerlIO_printf(DBILOGFP, "       bind %s <== %s (type %ld",
+       PerlIO_printf(DBILOGFP, "dbd_bind_ph(): bind %s <== %s (type %ld",
                name, neatsvpv(newvalue,0), (long)sql_type);
        if (is_inout)
            PerlIO_printf(DBILOGFP, ", inout 0x%lx, maxlen %ld",
@@ -1551,6 +2005,7 @@
     phs_svp = hv_fetch(imp_sth->all_params_hv, name, name_len, 0);
     if (phs_svp == NULL)
        croak("Can't bind unknown placeholder '%s' (%s)", name, 
neatsvpv(ph_namesv,0));
+    /* This value is not a string, but a binary structure phs_st instead. */
     phs = (phs_t*)(void*)SvPVX(*phs_svp);      /* placeholder struct   */
 
     if (phs->sv == &sv_undef) {        /* first bind for this placeholder      
*/
@@ -1563,7 +2018,15 @@
                imp_sth->out_params_av = newAV();
            av_push(imp_sth->out_params_av, SvREFCNT_inc(*phs_svp));
        }
-
+       /*
+        * Init number of bound array entries to zero.
+        * If "ora_maxarray_numentries" bind parameter specified,
+        * it would be set below.
+        *
+        * If no ora_maxarray_numentries specified, let it be
+        * the same as scalar(@array) bound.
+        */
+       phs->array_numstruct=0;
        if (attribs) {  /* only look for ora_type on first bind of var  */
            SV **svp;
            /* Setup / Clear attributes as defined by attribs.          */
@@ -1589,6 +2052,9 @@
            if ( (svp=hv_fetch((HV*)SvRV(attribs), "ora_maxdata_size", 16, 0)) 
!= NULL) {
                phs->maxdata_size = SvUV(*svp);
            }
+           if ( (svp=hv_fetch((HV*)SvRV(attribs), "ora_maxarray_numentries", 
23, 0)) != NULL) {
+               phs->ora_maxarray_numentries=SvUV(*svp);
+           }
        }
        if (sql_type)
            phs->ftype = ora_sql_type(imp_sth, phs->name, (int)sql_type);
@@ -1835,20 +2301,35 @@
            /* phs->alen has been updated by Oracle to hold the length of the 
result */
            phs_t *phs = 
(phs_t*)(void*)SvPVX(AvARRAY(imp_sth->out_params_av)[i]);
            SV *sv = phs->sv;
+           if (debug >= 2) {
+               PerlIO_printf(DBILOGFP,
+                       "dbd_st_execute(): Analyzing inout parameter '%s'\n",
+                       phs->name);
+           }
 
            if (phs->out_prepost_exec) {
                if (!phs->out_prepost_exec(sth, imp_sth, phs, 0))
                    return -2; /* out_prepost_exec already called ora_error()   
*/
+           }else{
+               if( SvROK(sv) ){
+                   if (SvTYPE(SvRV(sv))==SVt_PVAV) { 
+                       /* Array reference */
+                       dbd_phs_array_fixup_after_execute(phs);
+                   }
+                   /*
+                     [EMAIL PROTECTED]: Commented out.
+                     [EMAIL PROTECTED]: FIXME: What does this code mean ????
+
+                    else if (SvTYPE(sv) == SVt_RV && SvTYPE(SvRV(sv)) == 
SVt_PVAV) {
+                       AV *av = (AV*)SvRV(sv);
+                       I32 avlen = AvFILL(av);
+                       if (avlen >= 0)
+                           dbd_phs_avsv_complete(phs, avlen, debug);
+                   }*/
+               } else{
+                   dbd_phs_sv_complete(phs, sv, debug);
+               }
            }
-           else
-           if (SvTYPE(sv) == SVt_RV && SvTYPE(SvRV(sv)) == SVt_PVAV) {
-               AV *av = (AV*)SvRV(sv);
-               I32 avlen = AvFILL(av);
-               if (avlen >= 0)
-                   dbd_phs_avsv_complete(phs, avlen, debug);
-           }
-           else
-               dbd_phs_sv_complete(phs, sv, debug);
        }
     }
 
@@ -2261,6 +2742,22 @@
     if (phs->desc_h)
        OCIDescriptorFree_log(phs->desc_h, phs->desc_t);
 
+    if( phs->array_buf ){
+       free(phs->array_buf);
+       phs->array_buf=NULL;
+    }
+    if( phs->array_indicators ){
+       free(phs->array_indicators);
+       phs->array_indicators=NULL;
+    }
+    if( phs->array_lengths ){
+       free(phs->array_lengths);
+       phs->array_lengths=NULL;
+    }
+
+    phs->array_buflen=0;
+    phs->array_numallocated=0;
+
     sv_free(phs->ora_field);
     sv_free(phs->sv);
 }
Index: dbdimp.h
===================================================================
--- dbdimp.h    (revision 9916)
+++ dbdimp.h    (working copy)
@@ -105,7 +105,7 @@
     OCIStmt    *stmhp;
     ub2        stmt_type;      /* OCIAttrGet OCI_ATTR_STMT_TYPE        */
     U16                auto_lob;
-    int        has_lobs;
+    int        has_lobs;       /* Satement has bound LOBs */
 
     lob_refetch_t *lob_refetch;
     int        nested_cursor;  /* cursors fetched from SELECTs */
@@ -182,6 +182,10 @@
 };
 
 
+ /* Placeholder structure */
+ /* Note: phs_t is serialized into scalar value, and de-serialized then. */
+ /* Be carefull! */
+
 typedef struct phs_st phs_t;    /* scalar placeholder   */
 
 struct phs_st {        /* scalar placeholder EXPERIMENTAL      */
@@ -197,6 +201,7 @@
     bool is_inout;
 
     IV  maxlen;                /* max possible len (=allocated buffer) */
+                        /* Note: for array bind = buffer for each entry */
 
     OCIBind *bndhp;
     void *desc_h;      /* descriptor if needed (LOBs etc)      */
@@ -211,6 +216,16 @@
     int (*out_prepost_exec)_((SV *, imp_sth_t *, phs_t *, int pre_exec));
     SV *ora_field;     /* from attribute (for LOB binds)       */
     int alen_incnull;  /* 0 or 1 if alen should include null   */
+
+    /* Array bind support */
+    char   * array_buf;            /* Temporary buffer = malloc(array_buflen) 
*/
+    int      array_buflen;         /* Allocated length of array_buf */
+    int      array_numstruct;      /* Number of bound structures in buffer */
+    OCIInd * array_indicators;     /* Indicator array       = malloc( 
array_numallocated * sizeof(OCIInd) ) */
+    unsigned short *array_lengths; /* Array entries lengths = malloc( 
array_numallocated * sizeof(unsigned short) ) */
+    int      array_numallocated;   /* Allocated number of indicators/lengths */
+    int      ora_maxarray_numentries; /* Number of entries to send allocated 
to Oracle. (may be less, than total allocated) */
+    
     char name[1];      /* struct is malloc'd bigger as needed  */
 };
 

Reply via email to