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);
+		    }
+		    /*
+		      alex@alemate.ru: Commented out.
+		      alex@alemate.ru: 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	*/
 };
 
