On 11/01/2013 19:28, Tim Bunce wrote:
On Fri, Jan 11, 2013 at 04:04:13PM +0000, Martin J. Evans wrote:
My second sub attempt was to outright lie and set dbd_describe_done
and leave Active off so from perl land I just need to test Active
flag. This works and is a safer change since it ONLY applies to sth
handles magicked into existence for returned cursors. Also, if you
attempt to do anything else with the sth it errors as it should:
DBD::Oracle::st fetch failed: ERROR no statement executing (perhaps
you need to call execute first) at bz1245.pl line 16.
Wondered if anyone else had any thoughts on this.
Sounds good to me. Thanks for looking after this Martin.
Tim.
I've now got 2 ways to fix this issue. The first way is described above
and is a relatively small change. When pp_exec_rset is called post
execute it simply looks at the Oracle statement state and if it is only
initialised and not executed it leaves Active off and sets done_desc to
stop DBD::Oracle attempting to call dbd_describe. On the outside all
your Perl needs to do is test Active before attempting to use the cursor.
Advantages: small change unlikely to have any repercussions since we
still return a sth and if you attempt to use a non-executed sth it will
error with not executed. Fixes the problem I'm trying to fix.
Disadvantages: still creates a sth which is useless
Index: dbdimp.c
===================================================================
--- dbdimp.c (revision 15554)
+++ dbdimp.c (working copy)
@@ -2737,10 +2737,11 @@
DBIc_LOGPIO(imp_sth),
" pp_exec_rset bind %s - allocated %s...\n",
phs->name, neatsvpv(phs->sv, 0));
-
}
else { /* post-execute - setup the statement handle */
dTHR;
+ ub4 stmt_state = 99;
+ sword status;
SV * sth_csr = phs->sv;
D_impdata(imp_sth_csr, imp_sth_t, sth_csr);
@@ -2771,7 +2772,23 @@
imp_sth_csr->stmt_type = OCI_STMT_SELECT;
DBIc_IMPSET_on(imp_sth_csr);
- /* set ACTIVE so dbd_describe doesn't do explicit OCI describe */
+ OCIAttrGet_stmhp_stat(imp_sth_csr, &stmt_state, 0,
OCI_ATTR_STMT_STATE,
+ if (status != OCI_SUCCESS) {
+ oci_error(sth, imp_sth->errhp, status, "OCIAttrGet
OCI_ATTR_STMT_ST
+ return 0;
+ }
+ if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 ) {
+ /* initialized=1, executed=2, end of fetch=3 */
+ PerlIO_printf(
+ DBIc_LOGPIO(imp_sth),
+ " statement state: %u\n", stmt_state);
+ }
+ if (stmt_state == OCI_STMT_STATE_INITIALIZED) {
+ imp_sth_csr->done_desc = 1;
+ return 1;
+ }
+
+ /* set ACTIVE so dbd_describe doesn't do explicit OCI describe */
DBIc_ACTIVE_on(imp_sth_csr);
if (!dbd_describe(sth_csr, imp_sth_csr)) {
return 0;
Second solution is a bit more involved but I think better since a
non-executed sth is not returned - instead undef is returned.
Advantages: fixes problem and does not create a useless sth
Disadvantages: touches the code which gets run if the returned cursor is
executed although I've mainly just moved it to the post execute path.
Index: dbdimp.c
===================================================================
--- dbdimp.c (revision 15554)
+++ dbdimp.c (working copy)
@@ -2666,10 +2666,6 @@
dTHX;
if (pre_exec) { /* pre-execute - allocate a statement handle */
- dSP;
- D_imp_dbh_from_sth;
- HV *init_attr = newHV();
- int count;
sword status;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose
>= 3 )
@@ -2691,7 +2687,6 @@
OCIHandleAlloc_ok(imp_sth, imp_sth->envhp,
&phs->desc_h, phs->desc_t, status);
}
-
phs->progv = (char*)&phs->desc_h;
phs->maxlen = 0;
@@ -2714,6 +2709,38 @@
return 0;
}
+ }
+ else { /* post-execute - setup the statement handle */
+ dTHR;
+ dSP;
+ D_imp_dbh_from_sth;
+ HV *init_attr = newHV();
+ int count;
+ ub4 stmt_state = 99;
+ sword status;
+ SV * sth_csr;
+
+ /* Before we go to the bother of attempting to allocate a new sth
+ for this cursor make sure the Oracle sth is executed i.e.,
+ the returned cursor may never have been opened */
+ OCIAttrGet_stmhp_stat2(imp_sth, (OCIStmt*)phs->desc_h,
&stmt_state, 0,
+ OCI_ATTR_STMT_STATE, status);
+ if (status != OCI_SUCCESS) {
+ oci_error(sth, imp_sth->errhp, status, "OCIAttrGet
OCI_ATTR_STMT_STATE");
+ return 0;
+ }
+ if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 ) {
+ /* initialized=1, executed=2, end of fetch=3 */
+ PerlIO_printf(
+ DBIc_LOGPIO(imp_sth),
+ " returned cursor/statement state: %u\n", stmt_state);
+ }
+ if (stmt_state == OCI_STMT_STATE_INITIALIZED) {
+ phs->sv = newSV(0); /* undef */
+ return 1;
+ }
+
+ /* Now we know we have an executed cursor create a new sth */
ENTER;
SAVETMPS;
PUSHMARK(SP);
@@ -2738,11 +2765,7 @@
" pp_exec_rset bind %s - allocated %s...\n",
phs->name, neatsvpv(phs->sv, 0));
- }
- else { /* post-execute - setup the statement handle */
- dTHR;
- SV * sth_csr = phs->sv;
- D_impdata(imp_sth_csr, imp_sth_t, sth_csr);
+ sth_csr = phs->sv;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose
>= 3 )
PerlIO_printf(
@@ -2750,32 +2773,36 @@
" bind %s - initialising new %s for cursor
0x%lx...\n",
phs->name, neatsvpv(sth_csr,0), (unsigned
long)phs->progv);
- /* copy appropriate handles and atributes from parent
statement */
- imp_sth_csr->envhp = imp_sth->envhp;
- imp_sth_csr->errhp = imp_sth->errhp;
- imp_sth_csr->srvhp = imp_sth->srvhp;
- imp_sth_csr->svchp = imp_sth->svchp;
- imp_sth_csr->auto_lob = imp_sth->auto_lob;
- imp_sth_csr->pers_lob = imp_sth->pers_lob;
- imp_sth_csr->clbk_lob = imp_sth->clbk_lob;
- imp_sth_csr->piece_size = imp_sth->piece_size;
- imp_sth_csr->piece_lob = imp_sth->piece_lob;
- imp_sth_csr->is_child = 1; /*no prefetching on a
cursor or sp*/
+ {
+ D_impdata(imp_sth_csr, imp_sth_t, sth_csr); /* TO_DO */
+ /* copy appropriate handles and atributes from parent
statement */
+ imp_sth_csr->envhp = imp_sth->envhp;
+ imp_sth_csr->errhp = imp_sth->errhp;
+ imp_sth_csr->srvhp = imp_sth->srvhp;
+ imp_sth_csr->svchp = imp_sth->svchp;
+ imp_sth_csr->auto_lob = imp_sth->auto_lob;
+ imp_sth_csr->pers_lob = imp_sth->pers_lob;
+ imp_sth_csr->clbk_lob = imp_sth->clbk_lob;
+ imp_sth_csr->piece_size = imp_sth->piece_size;
+ imp_sth_csr->piece_lob = imp_sth->piece_lob;
+ imp_sth_csr->is_child = 1; /*no prefetching on a
cursor or sp*/
- /* assign statement handle from placeholder descriptor */
- imp_sth_csr->stmhp = (OCIStmt*)phs->desc_h;
- phs->desc_h = NULL; /* tell phs that we
own it now */
- /* force stmt_type since OCIAttrGet(OCI_ATTR_STMT_TYPE) doesn't
work! */
- imp_sth_csr->stmt_type = OCI_STMT_SELECT;
- DBIc_IMPSET_on(imp_sth_csr);
+ /* assign statement handle from placeholder descriptor */
+ imp_sth_csr->stmhp = (OCIStmt*)phs->desc_h;
+ phs->desc_h = NULL; /* tell phs that we
own it now */
- /* set ACTIVE so dbd_describe doesn't do explicit OCI describe */
- DBIc_ACTIVE_on(imp_sth_csr);
- if (!dbd_describe(sth_csr, imp_sth_csr)) {
- return 0;
- }
+ /* force stmt_type since OCIAttrGet(OCI_ATTR_STMT_TYPE)
doesn't work! */
+ imp_sth_csr->stmt_type = OCI_STMT_SELECT;
+ DBIc_IMPSET_on(imp_sth_csr);
+
+ /* set ACTIVE so dbd_describe doesn't do explicit OCI
describe */
+ DBIc_ACTIVE_on(imp_sth_csr);
+ if (!dbd_describe(sth_csr, imp_sth_csr)) {
+ return 0;
+ }
+ }
}
return 1;
Index: ocitrace.h
===================================================================
--- ocitrace.h (revision 15554)
+++ ocitrace.h (working copy)
@@ -293,9 +293,9 @@
#define OCIAttrGet_log_stat(impxxh, th,ht,ah,sp,at,eh,stat) \
stat = OCIAttrGet(th,ht,ah,sp,at,eh); \
(DBD_OCI_TRACEON(impxxh)) ?
PerlIO_printf(DBD_OCI_TRACEFP(impxxh), \
- "%sAttrGet(%p,%s,%p,%p,%s,%p)=%s\n", \
+ "%sAttrGet(%p,%s,%p,%p,%s,%p)=%s (%s,
%d)\n", \
OciTp,
(void*)th,oci_hdtype_name(ht),(void*)ah,pul_t(sp),oci_attr_name(at),(void*)eh,\
- oci_status_name(stat)),stat : stat
+ oci_status_name(stat), __FILE__, __LINE__),stat : stat
#define OCIAttrGet_d_log_stat(impsth, th,ht,ah,sp,at,eh,stat) \
stat = OCIAttrGet(th,ht,ah,sp,at,eh); \
@@ -317,6 +317,10 @@
OCIAttrGet_log_stat(imp_sth, imp_sth->stmhp,
OCI_HTYPE_STMT, \
(void*)(p1), (l), (a), imp_sth->errhp, stat)
+#define OCIAttrGet_stmhp_stat2(imp_sth, stmhp, p1, l, a,
stat) \
+ OCIAttrGet_log_stat(imp_sth, stmhp, OCI_HTYPE_STMT, \
+ (void*)(p1), (l), (a), imp_sth->errhp, stat)
+
#define OCIAttrSet_log_stat(impxxh,th,ht,ah,s1,a,eh,stat) \
stat=OCIAttrSet(th,ht,ah,s1,a,eh); \
(DBD_OCI_TRACEON(impxxh)) ?
PerlIO_printf(DBD_OCI_TRACEFP(impxxh), \
martin@bragi:~/svn/dbd-oracle/trunk$
Both pass all of the test suite and currently work with our application
which binds a lot of output cursors.
Obviously I favour the second one as less work is done when no executed
cursor is returned. However, I'd be happier if other people using output
cursors tried it.
All patches against subversion trunk.
Martin