Author: byterock
Date: Wed Mar 19 08:27:23 2008
New Revision: 10946

Added:
   dbd-oracle/trunk/t/51scroll.t
Modified:
   dbd-oracle/trunk/Changes
   dbd-oracle/trunk/Oracle.h
   dbd-oracle/trunk/Oracle.pm
   dbd-oracle/trunk/Oracle.xs
   dbd-oracle/trunk/dbdimp.c
   dbd-oracle/trunk/dbdimp.h
   dbd-oracle/trunk/oci8.c
   dbd-oracle/trunk/ocitrace.h

Log:
Changed the way pre-fetching is done by John Scoles
Added support for Scrollable cursors from John Scoles
Changed the max size of cache_rows to a ub4 rather than a ub2 from John Scoles


Modified: dbd-oracle/trunk/Changes
==============================================================================
--- dbd-oracle/trunk/Changes    (original)
+++ dbd-oracle/trunk/Changes    Wed Mar 19 08:27:23 2008
@@ -1,4 +1,7 @@
 =head1 Changes in DBD-Oracle 1.21(svn rev xxxx) 
+  Changed the way pre-fetching is done by John Scoles
+  Added support for Scollable cursors from John Scoles
+  Changed the max size of cache_rows to a ub4 rather than a ub2 from John 
Scoles
   Added support for Lobs in select of OCI Embedded Objects from John Scoles 
with a big thankyou to  Paul Weiss 
   Fixed for embedded object in object from Paul Weiss
   Added support for direct insert of large XML data into XMLType fields from 
Hendrik Fuss

Modified: dbd-oracle/trunk/Oracle.h
==============================================================================
--- dbd-oracle/trunk/Oracle.h   (original)
+++ dbd-oracle/trunk/Oracle.h   Wed Mar 19 08:27:23 2008
@@ -99,4 +99,5 @@
 #define ORA_VARCHAR2_TABLE     201
 #define ORA_NUMBER_TABLE       202
 #define ORA_NTY                            108
+
 /* end of Oracle.h */

Modified: dbd-oracle/trunk/Oracle.pm
==============================================================================
--- dbd-oracle/trunk/Oracle.pm  (original)
+++ dbd-oracle/trunk/Oracle.pm  Wed Mar 19 08:27:23 2008
@@ -22,13 +22,18 @@
            ORA_VARCHAR2 ORA_STRING ORA_NUMBER ORA_LONG ORA_ROWID ORA_DATE
            ORA_RAW ORA_LONGRAW ORA_CHAR ORA_CHARZ ORA_MLSLABEL ORA_NTY
            ORA_CLOB ORA_BLOB ORA_RSET ORA_VARCHAR2_TABLE ORA_NUMBER_TABLE
-           SQLT_INT SQLT_FLT XMLType
+           SQLT_INT SQLT_FLT  ORA_OCI      
        ) ],
         ora_session_modes => [ qw( ORA_SYSDBA ORA_SYSOPER ) ],
+        ora_fetch_orient  => [ qw( OCI_FETCH_NEXT OCI_FETCH_CURRENT 
OCI_FETCH_FIRST 
+                                  OCI_FETCH_LAST OCI_FETCH_PRIOR 
OCI_FETCH_ABSOLUTE 
+                                  OCI_FETCH_RELATIVE)],
+       ora_exe_modes     => [ qw(OCI_STMT_SCROLLABLE_READONLY)],
     );
-    @EXPORT_OK = qw(ORA_OCI SQLCS_IMPLICIT SQLCS_NCHAR ora_env_var 
ora_cygwin_set_env);
+    @EXPORT_OK = qw(OCI_FETCH_NEXT OCI_FETCH_CURRENT OCI_FETCH_FIRST 
OCI_FETCH_LAST OCI_FETCH_PRIOR
+                   OCI_FETCH_ABSOLUTE  OCI_FETCH_RELATIVE ORA_OCI 
SQLCS_IMPLICIT SQLCS_NCHAR ora_env_var ora_cygwin_set_env);
     #unshift @EXPORT_OK, 'ora_cygwin_set_env' if $^O eq 'cygwin';
-    Exporter::export_ok_tags(qw(ora_types ora_session_modes));
+    Exporter::export_ok_tags(qw(ora_types ora_session_modes ora_fetch_orient 
ora_exe_modes));
 
     my $Revision = substr(q$Revision: 1.103 $, 10);
 
@@ -68,7 +73,9 @@
         DBD::Oracle::db->install_method("ora_lob_length");
         DBD::Oracle::db->install_method("ora_nls_parameters");
         DBD::Oracle::db->install_method("ora_can_unicode");
-
+       DBD::Oracle::st->install_method("ora_fetch_scroll");
+       DBD::Oracle::st->install_method("ora_scroll_position");
+       
        $drh;
     }
 
@@ -874,7 +881,7 @@
         $$hash_of_arrays{$p_id} = $value_array;
        return ora_bind_param_inout_array($sth, $p_id, $value_array,$maxlen, 
$attr);
        1;
-       
+
     }
     
     
@@ -929,7 +936,7 @@
                  ora_placeholders      => undef,
                  ora_auto_lob          => undef,
                  ora_check_sql         => undef
-                 };
+                };
     }
    
 }
@@ -1193,7 +1200,7 @@
   ORA_VARCHAR2 ORA_STRING ORA_NUMBER ORA_LONG ORA_ROWID ORA_DATE
   ORA_RAW ORA_LONGRAW ORA_CHAR ORA_CHARZ ORA_MLSLABEL ORA_NTY
   ORA_CLOB ORA_BLOB ORA_RSET ORA_VARCHAR2_TABLE ORA_NUMBER_TABLE
-  SQLT_INT SQLT_FLT XMLType
+  SQLT_INT SQLT_FLT 
  
 =item SQLCS_IMPLICIT
 
@@ -1210,7 +1217,6 @@
 bind type for ORA_NUMBER_TABLE. See notes about ORA_NUMBER_TABLE elsewhere
 in this document
 
-
 =item ORA_OCI
 
 Oracle doesn't provide a formal API for determining the exact version
@@ -1455,6 +1461,18 @@
 
 See L</Prepare postponed till execute> for more information.
 
+=item ora_exe_mode
+
+This will set the execute mode of the current statement. Presently only one 
mode is supported;
+
+   OCI_STMT_SCROLLABLE_READONLY = 'scrollable results sets'
+
+=item ora_prefetch_memory
+
+Sets the memory level for top level rows to be prefetched. Rows up to the 
specified top level row 
+count C<RowCacheSize> are fetched if it occupies no more than the specified 
memory usage limit. The default value is 0, 
+which means that memory size is not included in computing the number of rows 
to prefetch.
+
 =back
 
 =head2 Placeholder Binding Attributes
@@ -1482,7 +1500,7 @@
 
 Additional values when DBD::Oracle was built using OCI 8 and later:
 
-  ORA_CLOB, ORA_BLOB, ORA_NTY, ORA_VARCHAR2_TABLE, ORA_NUMBER_TABLE, XMLType
+  ORA_CLOB, ORA_BLOB, ORA_NTY, ORA_VARCHAR2_TABLE, ORA_NUMBER_TABLE
 
 See L</Binding Cursors> for the correct way to use ORA_RSET.
 
@@ -2446,6 +2464,218 @@
 
 Set L</ora_check_sql> to 0 in prepare() to enable this behaviour.
 
+=head1 Scrollable Cursors
+
+Oracle supports the concept of a 'Scrollable Cursor' which is defined as a 
'result set' where
+the rows can be fetched either sequentially or non-sequentially. One can fetch 
rows forward, 
+backwards, from any given position or the n-th row from the current position 
in the 'result set'.
+
+Rows are numbered sequentially starting at one and client-side caching of the 
partial or entire result set
+can improve performance by limiting round trips to the server.
+
+Oracle does not support DML type operations with scrollable cursors so you are 
limited
+to simple 'Select' operations only. As well you can not use this functionality 
with remote 
+mapped queries or if the LONG datatype is part of the select list. 
+
+However, Lobs, Clobs, and Blobs do work.  
+
+Only use scrollable cursors if you really have a good reason to. They do use 
up considerable 
+more server and client resources and have poorer response times than 
non-scrolling cursors.
+
+=head2 Enabling Scrollable Cursors
+
+To enable this functionality you must first import the "Fetch Orientation" and 
the 'Execution Mode' constants by using
+
+   use DBD::Oracle qw(:ora_fetch_orient,:ora_exe_modes);
+  
+Which will import the following fetch orientation constants;
+
+  OCI_FETCH_CURRENT  - gets the current row.
+  OCI_FETCH_NEXT     - gets the next row from the current position.
+  OCI_FETCH_FIRST    - gets the first row in the 'result set'
+  OCI_FETCH_LAST     - gets the last row in  the 'result set'
+  OCI_FETCH_PRIOR    - positions the result set on the previous row from the 
current row in the 'result set'
+  OCI_FETCH_ABSOLUTE - will fetch a row number in the result set using 
absolute positioning
+  OCI_FETCH_RELATIVE - will fetch a row number in the result set using 
relative positioning
+  
+and the following Execution Mode constant;
+
+  OCI_STMT_SCROLLABLE_READONLY - Required to make the result set scrollable.
+
+Next you will have to tell DBD::Oracle that you will be using scrolling by 
setting the ora_exe_mode attribute on the
+statement handle to 'OCI_STMT_SCROLLABLE_READONLY' with the prepare method;
+
+  $sth=$dbh->prepare($sql,{ora_exe_mode=>OCI_STMT_SCROLLABLE_READONLY});
+
+When the statement is executed you will then be able to use ora_fetch_scroll 
method to get your results 
+or you can still use any of the other fetch methods but with a poorer response 
time than if you used a 
+non-scrolling cursor.
+
+=head2 Scrollable Cursor Methods
+
+The following driver-specific methods are used with scrollable cursors.
+
+=over 4
+
+=item ora_fetch_scroll
+
+  my $value =  $sth->ora_fetch_scroll($fetch_orient,$fetch_offset);
+
+Works the same as fetchrow_array method however, one passes in a "Fetch 
Orientation" constant and a fetch_offset 
+value which will then determine the row that will be fetched. It returns the 
row as a list containing the field values. 
+Null fields are returned as undef values in the list.
+
+=item ora_scroll_position
+
+  my $position =  $sth->ora_scroll_position();
+      
+This method returns the current position in the result set.
+
+=head2 Scrollable Cursor Usage
+
+Given a simple code like this:
+
+   use DBI;
+   use DBD::Oracle qw(:ora_types);
+   my $dbh = DBI->connect($dsn, $dbuser, '');
+   my $sql = "select id,
+                     first_name,
+                     last_name
+                from employee";
+   my $sth=$dbh->prepare($sql,{ora_exe_mode=>OCI_STMT_SCROLLABLE_READONLY});
+   $sth->execute();
+   my $value;
+
+and one assumes that the number of rows returned from the query 20, the code 
snippets below will illustrate the use of ora_fetch_scroll
+method.
+
+=over 4
+
+=item Fetching the Current Row
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_CURRENT,0);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This will get the current row of the result set, which in this case would be 
1, print the values for the fields in the row 
+and then print the current scroll position which would still be 1. With this 
fetch constant the value for fetch offset is ignored.
+
+=item Fetching the Next Row
+
+   for(my i=0;i<=3;i++){
+      $value =  $sth->ora_fetch_scroll(OCI_FETCH_NEXT,0);
+      print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   }
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will get the next four rows from the result (2,3,4,5) set and 
print them and then print the position which will be 5, 
+Again the value for fetch offset is ignored for this fetch constant.
+
+=item Fetching the First Row
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_FIRST,0);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will move the pointer back to the first row (1) and print out its 
values again. The position value will be 1
+in this case. Again the value for fetch offset is ignored for this fetch 
constant.
+
+=item Fetching the Last Row
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_LAST,0);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will move the pointer to the last row in the result set (20) and 
print out its values. The position value will be 20
+in this case. The value for fetch offset is ignored for this fetch constant. 
This is also way to determine the number of rows in 
+the record set, however if you result set is large response time can be high.
+
+=item Fetching the Prior Row
+
+   for(my $i=0;$i<=3;i++){
+      $value =  $sth->ora_fetch_scroll(OCI_FETCH_PRIOR,0);
+      print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   }
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will get the four prior rows from the result set (19,18,17,16) 
and print them and then print 
+the position which will be 16. Again the value for fetch offset is ignored for 
this fetch constant.
+
+=item Fetching the 10th Row
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_ABSOLUTE,10);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will move the pointer to row 10 and print out its values. The 
position value will be 10
+in this case. With this fetch constant the fetch offset value is the row 
number to fetch. 
+
+=item Fetching the 10th to 14th Row
+
+   for(my $i=10;$i<15;i++){
+      $value =  $sth->ora_fetch_scroll(OCI_FETCH_ABSOLUTE,$i);
+      print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   }
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+  
+In this snippet the OCI_FETCH_ABSOLUTE constant is used with the offset 
variable $i to get rows 10,11,12,13, and 14
+from the record set and print them out. The position value will be 14 at the 
end of this code.
+
+=item Fetching the 14th to 10th Row
+
+   for(my $i=14;$i>9;i--){
+      $value =  $sth->ora_fetch_scroll(OCI_FETCH_ABSOLUTE,$i);
+      print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   }
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+  
+In this snippet the OCI_FETCH_ABSOLUTE constant is used with the offset 
variable $i to get rows 14,13,12,11, and 10 
+from the record set and print them out. The position value will be 10 at the 
end of this code.
+
+=item Fetching the 5th Row From the Present position
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_RELATIVE,5);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will jump forward in the record set by the offset value of '5', 
from row 10 to row 15, and print out its values. 
+The position value will be 15 at this point. With this fetch constant the 
fetch offset value is the relative row from the current row to fetch. 
+
+=item Fetching the 9th Row Prior From the Present position
+
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_RELATIVE,-9);
+   print "id=".$value->[0].", First Name=".$value->[1].", Last 
Name=".$value->[2]."\n";
+   print "current scroll position=".$sth->ora_scroll_position()."\n";
+   
+This snippet will jump backward in the record set by the offset value of '-9', 
from row 15 to row 6 and print out its values. 
+The position value will be 6 at this point. 
+
+=item Relative Fetching Equivalents
+
+When using OCI_FETCH_RELATIVE with a fetch offset equal to 0 will get the 
current row which is the same as a OCI_FETCH_CURRENT fetch.
+When using OCI_FETCH_RELATIVE with a fetch offset equal to 1 will get the next 
row which is the same as a OCI_FETCH_NEXT fetch.
+When using OCI_FETCH_RELATIVE with a fetch offset equal to -1 will get the 
prior row which is the same as a OCI_FETCH_PRIOR fetch.
+
+=item Use Finish
+
+   $sth->finish();
+   
+When using scrollable cursors it is required that you use the $sth->finish() 
method when you are done with the cursor as this type of
+cursor has to be explicitly canceled on the server. If you do not do this you 
may cause resource problems on your database.  
+
+=head2 Prefetching Rows
+
+One can override the DBD::Oracle's default pre-fetch values by using the DBI 
database handle attribute C<RowCacheSize> and or the 
+Prepare Attribute 'ora_prefetch_memory'. Tweaking these values may yield 
improved performance. 
+
+   $dbh->{RowCacheSize} = 10;
+   
$sth=$dbh->prepare($sql,{ora_exe_mode=>OCI_STMT_SCROLLABLE_READONLY,ora_prefetch_memory=>10000});
+
+In the above example 10 rows will be prefetched up to a maximum of 10000 bytes 
of data.  A good RowCacheSize value for a scrollable cursor
+is about 20% of expected size of the record set. If the ora_prefetch_memory is 
0 or not present then memory size is not included 
+in computing the number of rows to prefetch otherwise the number of rows will 
be limited to memory size. 
+ 
+
 =head1 Handling LOBs
 
 =head2 Simple Usage
@@ -2463,9 +2693,9 @@
 To correct for this you must use an SQL UPDATE statement to reset the 
 LOB column to a non-NULL (or empty) value with an SQL like this;
 
-     UPDATE lob_example 
-        SET bindata=EMPTY_BLOB()
-      WHERE bindata IS NULL.
+  UPDATE lob_example 
+     SET bindata=EMPTY_BLOB()
+   WHERE bindata IS NULL.
 
 When fetching LOBs they are, by default, made to look just like LONGs and
 are subject to the LongReadLen and LongTruncOk attributes. Note that
@@ -3221,7 +3451,7 @@
 
 Any NULL values found in the embedded object will be returned as 'undef'.
 
-=head1 Support for Insert of XMLType
+=head1 Support for Insert of XMLType (ORA_NTY)
 
 Inserting large XML data sets into tables with XMLType fields is now supported 
by DBD::Oracle. The only special 
 requirement is the use of bind_param() with an attribute hash parameter that 
specifies ora_type as ORA_NTY. For

Modified: dbd-oracle/trunk/Oracle.xs
==============================================================================
--- dbd-oracle/trunk/Oracle.xs  (original)
+++ dbd-oracle/trunk/Oracle.xs  Wed Mar 19 08:27:23 2008
@@ -16,23 +16,41 @@
     ORA_LONG    =   8
     ORA_ROWID   =  11
     ORA_DATE    =  12
-    ORA_RAW     =  23
+    ORA_RAW             =  23
     ORA_LONGRAW         =  24
     ORA_CHAR    =  96
     ORA_CHARZ   =  97
     ORA_MLSLABEL = 105
-    ORA_NTY     = 108
+    ORA_NTY             = 108
     ORA_CLOB    = 112
     ORA_BLOB    = 113
     ORA_RSET    = 116
     ORA_VARCHAR2_TABLE = ORA_VARCHAR2_TABLE
     ORA_NUMBER_TABLE   = ORA_NUMBER_TABLE
-    ORA_SYSDBA  = 0x0002
-    ORA_SYSOPER         = 0x0004
-    SQLCS_IMPLICIT = SQLCS_IMPLICIT
-    SQLCS_NCHAR    = SQLCS_NCHAR
-    SQLT_INT     = SQLT_INT
-    SQLT_FLT     = SQLT_FLT    
+    ORA_SYSDBA                   = 0x0002
+    ORA_SYSOPER                          = 0x0004
+    SQLCS_IMPLICIT               = SQLCS_IMPLICIT
+       SQLCS_NCHAR               = SQLCS_NCHAR
+       SQLT_INT                  = SQLT_INT
+       SQLT_FLT                  = SQLT_FLT
+       OCI_BATCH_MODE        = 0x01
+       OCI_EXACT_FETCH       = 0x02
+       OCI_KEEP_FETCH_STATE  = 0x04
+       OCI_DESCRIBE_ONLY     = 0x10
+       OCI_COMMIT_ON_SUCCESS = 0x20
+       OCI_NON_BLOCKING      = 0x40
+       OCI_BATCH_ERRORS      = 0x80
+       OCI_PARSE_ONLY        = 0x100
+       OCI_SHOW_DML_WARNINGS = 0x400
+       OCI_STMT_SCROLLABLE_READONLY = 0x08
+       OCI_FETCH_CURRENT       = OCI_FETCH_CURRENT
+       OCI_FETCH_NEXT          = OCI_FETCH_NEXT
+       OCI_FETCH_FIRST         = OCI_FETCH_FIRST
+       OCI_FETCH_LAST          = OCI_FETCH_LAST
+       OCI_FETCH_PRIOR         = OCI_FETCH_PRIOR
+       OCI_FETCH_ABSOLUTE      = OCI_FETCH_ABSOLUTE
+       OCI_FETCH_RELATIVE      = OCI_FETCH_RELATIVE
+
     CODE:
     if (!ix) {
        if (!name) name = GvNAME(CvGV(cv));
@@ -77,6 +95,40 @@
 
 MODULE = DBD::Oracle    PACKAGE = DBD::Oracle::st
 
+
+void
+ora_scroll_position(sth)
+    SV *       sth
+    PREINIT:
+    D_imp_sth(sth);
+   CODE:
+    {
+       XSRETURN_IV( imp_sth->fetch_position);
+}
+
+void
+ora_fetch_scroll(sth,fetch_orient,fetch_offset)
+    SV *       sth
+    IV  fetch_orient
+    IV         fetch_offset
+    PREINIT:
+    D_imp_sth(sth);
+    CODE:
+    {
+    AV *av;
+  /*  SV **svp;
+    if (fetch_orient){
+       fetch_orient = OCI_FETCH_NEXT;
+    }
+    sb4 fetch_offset = 0;
+    DBD_ATTRIB_GET_IV(  attribs, "fetch_orient",12, svp, fetch_orient);
+    DBD_ATTRIB_GET_IV(  attribs, "fetch_offset",12, svp, fetch_offset);*/
+    imp_sth->fetch_orient=fetch_orient;
+    imp_sth->fetch_offset=fetch_offset;
+    av = dbd_st_fetch(sth,imp_sth);
+    ST(0) = (av) ? sv_2mortal(newRV((SV *)av)) : &PL_sv_undef;
+}
+
 void
 ora_bind_param_inout_array(sth, param, av_ref, maxlen, attribs)
     SV *       sth
@@ -110,7 +162,7 @@
     ST(0) = dbd_bind_ph(sth, imp_sth, param,av_value, sql_type, attribs, TRUE, 
maxlen)
                ? &sv_yes : &sv_no;
 }
-    
+
 void
 ora_fetch(sth)
     SV *       sth
@@ -227,7 +279,7 @@
         oci_error(dbh, imp_dbh->errhp, status, "OCILobCharSetForm");
        ST(0) = &sv_undef;
         return;
-    }    
+    }
 #ifdef OCI_ATTR_CHARSET_ID
     /* Effectively only used so AL32UTF8 works properly */
     OCILobCharSetId_log_stat( imp_dbh->envhp, imp_dbh->errhp, locator, &csid, 
status );
@@ -429,6 +481,6 @@
        dbd_init_oci(DBIS) ;
        dbd_init_oci_drh(imp_drh) ;
 
-    
 
-       
+
+

Modified: dbd-oracle/trunk/dbdimp.c
==============================================================================
--- dbd-oracle/trunk/dbdimp.c   (original)
+++ dbd-oracle/trunk/dbdimp.c   Wed Mar 19 08:27:23 2008
@@ -955,30 +955,32 @@
         retsv = boolSV(DBIc_has(imp_dbh,DBIcf_AutoCommit));
     }
     else if (kl==12 && strEQ(key, "RowCacheSize")) {
-       retsv = newSViv(imp_dbh->RowCacheSize);
+               retsv = newSViv(imp_dbh->RowCacheSize);
     }
     else if (kl==22 && strEQ(key, "ora_max_nested_cursors")) {
-       retsv = newSViv(imp_dbh->max_nested_cursors);
+               retsv = newSViv(imp_dbh->max_nested_cursors);
     }
     else if (kl==11 && strEQ(key, "ora_ph_type")) {
-       retsv = newSViv(imp_dbh->ph_type);
+               retsv = newSViv(imp_dbh->ph_type);
     }
     else if (kl==13 && strEQ(key, "ora_ph_csform")) {
-       retsv = newSViv(imp_dbh->ph_csform);
+               retsv = newSViv(imp_dbh->ph_csform);
     }
     else if (kl==22 && strEQ(key, "ora_parse_error_offset")) {
        retsv = newSViv(imp_dbh->parse_error_offset);
     }
     if (!retsv)
-       return Nullsv;
+               return Nullsv;
     if (cacheit) {     /* cache for next time (via DBI quick_FETCH)    */
-       SV **svp = hv_fetch((HV*)SvRV(dbh), key, kl, 1);
-       sv_free(*svp);
-       *svp = retsv;
-       (void)SvREFCNT_inc(retsv);      /* so sv_2mortal won't free it  */
+               SV **svp = hv_fetch((HV*)SvRV(dbh), key, kl, 1);
+               sv_free(*svp);
+               *svp = retsv;
+               (void)SvREFCNT_inc(retsv);      /* so sv_2mortal won't free it  
*/
     }
+    
     if (retsv == &sv_yes || retsv == &sv_no)
-       return retsv; /* no need to mortalize yes or no */
+               return retsv; /* no need to mortalize yes or no */
+               
     return sv_2mortal(retsv);
 }
 
@@ -2859,18 +2861,20 @@
     dTHR;
     dTHX;
     ub4 row_count = 0;
-    int debug = DBIS->debug;
+    int debug    = DBIS->debug;
     int outparams = (imp_sth->out_params_av) ? 
AvFILL(imp_sth->out_params_av)+1 : 0;
-
     D_imp_dbh_from_sth;
     sword status;
     int is_select = (imp_sth->stmt_type == OCI_STMT_SELECT);
+    ub4 exe_mode  = imp_sth->exe_mode;
+   
 
     if (debug >= 2)
-       PerlIO_printf(DBILOGFP, "    dbd_st_execute %s (out%d, lob%d)...\n",
+          PerlIO_printf(DBILOGFP, "    dbd_st_execute %s (out%d, lob%d)...\n",
            oci_stmt_type_name(imp_sth->stmt_type), outparams, 
imp_sth->has_lobs);
 
-    /* Don't attempt execute for nested cursor. It would be meaningless,
+
+   /* Don't attempt execute for nested cursor. It would be meaningless,
        and Oracle code has been seen to core dump */
     if (imp_sth->nested_cursor) {
                oci_error(sth, NULL, OCI_ERROR,
@@ -2932,15 +2936,19 @@
                        }
                        }
        }
-
-       OCIStmtExecute_log_stat(imp_sth->svchp, imp_sth->stmhp, imp_sth->errhp,
-                       (ub4)(is_select ? 0 : 1),
-                       0, 0, 0,
-                       /* we don't AutoCommit on select so LOB locators work */
-                       (ub4)((DBIc_has(imp_dbh,DBIcf_AutoCommit) && !is_select)
-                               ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT),
-                       status);
-
+ 
+         
+               if (DBIc_has(imp_dbh,DBIcf_AutoCommit) && !is_select) {
+                   imp_sth->exe_mode=OCI_COMMIT_ON_SUCCESS;
+                   /* we don't AutoCommit on select so LOB locators work */
+               }
+               
+           
+       
+               OCIStmtExecute_log_stat(imp_sth->svchp, imp_sth->stmhp, 
imp_sth->errhp,
+                                       (ub4)(is_select ? 0 : 1),
+                                       0, 0, 0,(ub4)imp_sth->exe_mode,status);
+                               
                if (status != OCI_SUCCESS) { /* may be OCI_ERROR or 
OCI_SUCCESS_WITH_INFO etc */
        /* we record the error even for OCI_SUCCESS_WITH_INFO */
                        oci_error(sth, imp_sth->errhp, status, 
ora_sql_error(imp_sth,"OCIStmtExecute"));
@@ -3396,7 +3404,7 @@
         PerlIO_printf(DBIc_LOGPIO(imp_sth), "    dbd_st_finish\n");
 
     if (!DBIc_ACTIVE(imp_sth))
-       return 1;
+               return 1;
 
     /* Cancel further fetches from this cursor.                 */
     /* We don't close the cursor till DESTROY (dbd_st_destroy). */
@@ -3411,16 +3419,22 @@
     }
 
     if (dirty)                 /* don't walk on the wild side  */
-       return 1;
+               return 1;
 
     if (!DBIc_ACTIVE(imp_dbh))         /* no longer connected  */
-       return 1;
+               return 1;
 
+    /*fetching on a cursor with row =0 will explicitly free any
+    server side resources this is what the next statment does,
+    not sure if we need this for non scrolling cursors they should die on
+    a OER(1403) no records)*/
+    
     OCIStmtFetch_log_stat(imp_sth->stmhp, imp_sth->errhp, 0,
-               OCI_FETCH_NEXT, OCI_DEFAULT, status);
+       OCI_FETCH_NEXT,0,  status);
+       
     if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) {
-       oci_error(sth, imp_sth->errhp, status, "Finish OCIStmtFetch");
-       return 0;
+               oci_error(sth, imp_sth->errhp, status, "Finish OCIStmtFetch");
+               return 0;
     }
     return 1;
 }

Modified: dbd-oracle/trunk/dbdimp.h
==============================================================================
--- dbd-oracle/trunk/dbdimp.h   (original)
+++ dbd-oracle/trunk/dbdimp.h   Wed Mar 19 08:27:23 2008
@@ -46,6 +46,7 @@
     int parse_error_offset;    /* position in statement of last error */
     int max_nested_cursors;     /* limit on cached nested cursors per stmt */
     int array_chunk_size;  /* the max size for an array bind */
+
 };
 
 #define DBH_DUP_OFF sizeof(dbih_dbc_t)
@@ -55,21 +56,21 @@
 typedef struct lob_refetch_st lob_refetch_t; /* Define sth implementor data 
structure */
 
 
-
+/*statement structure */
 struct imp_sth_st {
 
     dbih_stc_t com;            /* MUST be first element in structure   */
 
     void *(*get_oci_handle) _((imp_sth_t *imp_sth, int handle_type, int 
flags));
-    OCIEnv                      *envhp;        /* copy of dbh pointer  */
-    OCIError            *errhp;        /* copy of dbh pointer  */
-    OCIServer           *srvhp;        /* copy of dbh pointer  */
-    OCISvcCtx           *svchp;        /* copy of dbh pointer  */
-    OCIStmt                 *stmhp;    /* oci statement  handle */
-    OCIDescribe         *dschp;    /* oci describe handle */
-       ub2             stmt_type;      /* OCIAttrGet OCI_ATTR_STMT_TYPE        
*/
-    U16                        auto_lob;
-    int                has_lobs;  /* Statement has boud LOBS*/
+    OCIEnv                     *envhp; /* copy of dbh pointer  */
+    OCIError           *errhp; /* copy of dbh pointer  */
+    OCIServer          *srvhp; /* copy of dbh pointer  */
+    OCISvcCtx          *svchp; /* copy of dbh pointer  */
+    OCIStmt                    *stmhp; /* oci statement  handle */
+    OCIDescribe        *dschp; /* oci describe handle */
+       ub2                     stmt_type;      /* OCIAttrGet 
OCI_ATTR_STMT_TYPE        */
+    U16                                auto_lob;       /* use auto lobs*/
+    int                        has_lobs;   /* Statement has bound LOBS*/
 
     lob_refetch_t *lob_refetch;
     int                nested_cursor; /* cursors fetched from SELECTs */
@@ -98,7 +99,14 @@
     int        est_width;    /* est'd avg row width on-the-wire        */
     /* (In/)Out Parameter Details */
     bool               has_inout_params;
-
+    /* execute mode*/
+    /* will be using this alot later me thinks  */
+    ub4         exe_mode;
+    /* fetch scrolling values */
+    int                fetch_orient;
+    int                        fetch_offset;
+    int                        fetch_position;
+    int                prefetch_memory;   /* OCI_PREFETCH_MEMORY*/
 };
 #define IMP_STH_EXECUTING      0x0001
 

Modified: dbd-oracle/trunk/oci8.c
==============================================================================
--- dbd-oracle/trunk/oci8.c     (original)
+++ dbd-oracle/trunk/oci8.c     Wed Mar 19 08:27:23 2008
@@ -33,6 +33,7 @@
     imp_drh->ora_trunc   = perl_get_sv("Oraperl::ora_trunc",     GV_ADDMULTI);
     imp_drh->ora_cache   = perl_get_sv("Oraperl::ora_cache",     GV_ADDMULTI);
     imp_drh->ora_cache_o = perl_get_sv("Oraperl::ora_cache_o",   GV_ADDMULTI);
+    
 }
 
 
@@ -111,6 +112,29 @@
     return SvPV(sv,na);
 }
 
+/*used to look up the name of a fetchtype constant
+  used only for debugging */
+char *
+oci_fetch_options(ub4 fetchtype)
+{
+       dTHX;
+    SV *sv;
+    switch (fetchtype) {
+    /* fetch options */
+       case OCI_FETCH_CURRENT:     return "OCI_FETCH_CURRENT";
+       case OCI_FETCH_NEXT:        return "OCI_FETCH_NEXT";
+       case OCI_FETCH_FIRST:       return "OCI_FETCH_FIRST";
+       case OCI_FETCH_LAST:        return "OCI_FETCH_LAST";
+       case OCI_FETCH_PRIOR:       return "OCI_FETCH_PRIOR";
+       case OCI_FETCH_ABSOLUTE:    return "OCI_FETCH_ABSOLUTE";
+       case OCI_FETCH_RELATIVE:        return "OCI_FETCH_RELATIVE";
+    }
+    sv = sv_2mortal(newSViv((IV)fetchtype));
+    return SvPV(sv,na);
+}
+
+
+
 
 static sb4
 oci_error_get(OCIError *errhp, sword status, char *what, SV *errstr, int debug)
@@ -254,17 +278,17 @@
 {
     dTHX;
     D_imp_dbh_from_sth;
-    sword status = 0;
-    ub4   oparse_lng   = 1;  /* auto v6 or v7 as suits db connected to */
-    int   ora_check_sql = 1;   /* to force a describe to check SQL     */
-    IV    ora_placeholders = 1;        /* find and handle placeholders */
+    sword status                = 0;
+    ub4        oparse_lng       = 1;  /* auto v6 or v7 as suits db connected 
to        */
+    int ora_check_sql   = 1;   /* to force a describe to check SQL     */
+    IV  ora_placeholders = 1;  /* find and handle placeholders */
        /* XXX we set ora_check_sql on for now to force setup of the    */
        /* row cache. Change later to set up row cache using just a     */
        /* a memory size, perhaps also default $RowCacheSize to a       */
        /* negative value. OCI_ATTR_PREFETCH_MEMORY */
 
     if (!DBIc_ACTIVE(imp_dbh)) {
-       oci_error(sth, NULL, OCI_ERROR, "Database disconnected");
+               oci_error(sth, NULL, OCI_ERROR, "Database disconnected");
         return 0;
     }
 
@@ -274,30 +298,37 @@
     imp_sth->get_oci_handle = oci_st_handle;
 
     if (DBIc_COMPAT(imp_sth)) {
-       static SV *ora_pad_empty;
-       if (!ora_pad_empty) {
-           ora_pad_empty= perl_get_sv("Oraperl::ora_pad_empty", GV_ADDMULTI);
-           if (!SvOK(ora_pad_empty) && getenv("ORAPERL_PAD_EMPTY"))
-               sv_setiv(ora_pad_empty, atoi(getenv("ORAPERL_PAD_EMPTY")));
-       }
-       imp_sth->ora_pad_empty = (SvOK(ora_pad_empty)) ? SvIV(ora_pad_empty) : 
0;
+               static SV *ora_pad_empty;
+               if (!ora_pad_empty) {
+                   ora_pad_empty= perl_get_sv("Oraperl::ora_pad_empty", 
GV_ADDMULTI);
+                   if (!SvOK(ora_pad_empty) && getenv("ORAPERL_PAD_EMPTY"))
+                               sv_setiv(ora_pad_empty, 
atoi(getenv("ORAPERL_PAD_EMPTY")));
+               }
+               imp_sth->ora_pad_empty = (SvOK(ora_pad_empty)) ? 
SvIV(ora_pad_empty) : 0;
     }
 
     imp_sth->auto_lob = 1;
-    if (attribs) {
-       SV **svp;
-       IV ora_auto_lob = 1;
-       DBD_ATTRIB_GET_IV(  attribs, "ora_parse_lang", 14, svp, oparse_lng);
-       DBD_ATTRIB_GET_IV(  attribs, "ora_placeholders", 16, svp, 
ora_placeholders);
-       DBD_ATTRIB_GET_IV(  attribs, "ora_auto_lob",   12, svp, ora_auto_lob);
-       imp_sth->auto_lob = (ora_auto_lob) ? 1 : 0;
-       /* ora_check_sql only works for selects owing to Oracle behaviour */
-       DBD_ATTRIB_GET_IV(  attribs, "ora_check_sql",  13, svp, ora_check_sql);
-   }
+       imp_sth->exe_mode  = OCI_DEFAULT;
+       
+       if (attribs) {
+               SV **svp;
+               IV ora_auto_lob = 1;
+               DBD_ATTRIB_GET_IV(  attribs, "ora_parse_lang", 14, svp, 
oparse_lng);
+               DBD_ATTRIB_GET_IV(  attribs, "ora_placeholders", 16, svp, 
ora_placeholders);
+               DBD_ATTRIB_GET_IV(  attribs, "ora_auto_lob",   12, svp, 
ora_auto_lob);
+               imp_sth->auto_lob = (ora_auto_lob) ? 1 : 0;
+               /* ora_check_sql only works for selects owing to Oracle 
behaviour */
+               DBD_ATTRIB_GET_IV(  attribs, "ora_check_sql",  13, svp, 
ora_check_sql);
+               DBD_ATTRIB_GET_IV(  attribs, "ora_exe_mode",  12, svp, 
imp_sth->exe_mode);
+               DBD_ATTRIB_GET_IV(  attribs, "ora_prefetch_memory",  19, svp, 
imp_sth->prefetch_memory);
+  
+       }
 
-    /* scan statement for '?', ':1' and/or ':foo' style placeholders   */
+
+
+       /* scan statement for '?', ':1' and/or ':foo' style placeholders        
*/
     if (ora_placeholders)
-       dbd_preparse(imp_sth, statement);
+               dbd_preparse(imp_sth, statement);
     else imp_sth->statement = savepv(statement);
 
     imp_sth->envhp = imp_dbh->envhp;
@@ -306,11 +337,11 @@
     imp_sth->svchp = imp_dbh->svchp;
 
     switch(oparse_lng) {
-    case 0:  /* old: calls for V6 syntax - give them V7        */
-    case 2:  /* old: calls for V7 syntax               */
-    case 7:  oparse_lng = OCI_V7_SYNTAX;       break;
-    case 8:  oparse_lng = OCI_V8_SYNTAX;       break;
-    default: oparse_lng = OCI_NTV_SYNTAX;      break;
+       case 0:  /* old: calls for V6 syntax - give them V7     */
+       case 2:  /* old: calls for V7 syntax            */
+       case 7:  oparse_lng = OCI_V7_SYNTAX;    break;
+       case 8:  oparse_lng = OCI_V8_SYNTAX;    break;
+       default: oparse_lng = OCI_NTV_SYNTAX;   break;
     }
 
     OCIHandleAlloc_ok(imp_dbh->envhp, &imp_sth->stmhp, OCI_HTYPE_STMT, status);
@@ -340,7 +371,7 @@
     }
     else {
       /* set initial cache size by memory */
-      /* [I'm not now sure why this is here - from a patch sometime ago - Tim] 
*/
+      /* [I'm not now sure why this is here - from a patch sometime ago - 
Tim]*/
       ub4 cache_mem;
       IV cache_mem_iv;
       D_imp_dbh_from_sth ;
@@ -1264,30 +1295,31 @@
 {
        dTHX;
     if (has_longs)                     /* override/disable caching     */
-       cache_rows = 1;                 /* else read_blob can't work    */
+               cache_rows = 1;                 /* else read_blob can't work    
*/
     else
-    if (cache_rows == 0) {             /* automatically size the cache */
+           if (cache_rows == 0) {              /* automatically size the cache 
*/
 
-       /* Oracle packets on ethernet have max size of around 1460.     */
-       /* We'll aim to fill our row cache with around 10 per go.       */
-       /* Using 10 means any 'runt' packets will have less impact.     */
-       int txfr_size  = 10 * 1460;     /* desired transfer/cache size  */
-
-       /* Use guessed average on-the-wire row width calculated above & */
-       /* add in overhead of 5 bytes per field plus 8 bytes per row.   */
-       /* The n*5+8 was determined by studying SQL*Net v2 packets.     */
-       /* It could probably benefit from a more detailed analysis.     */
-       est_width += num_fields*5 + 8;
-
-       cache_rows = txfr_size / est_width;           /* (maybe 1 or 0) */
-
-       /* To ensure good performance with large rows (near or larger   */
-       /* than our target transfer size) we set a minimum cache size.  */
-       if (cache_rows < 6)     /* is cache a 'useful' size?    */
-           cache_rows = (cache_rows > 0) ? 6 : 4;
-    }
-    if (cache_rows > 32767)    /* keep within Oracle's limits  */
-       cache_rows = 32767;
+                       /* Oracle packets on ethernet have max size of around 
1460.     */
+                       /* We'll aim to fill our row cache with around 10 per 
go.       */
+                       /* Using 10 means any 'runt' packets will have less 
impact.     */
+                       int txfr_size  = 10 * 1460;     /* desired 
transfer/cache size  */
+
+                       /* Use guessed average on-the-wire row width calculated 
above & */
+                       /* add in overhead of 5 bytes per field plus 8 bytes 
per row.   */
+                       /* The n*5+8 was determined by studying SQL*Net v2 
packets.     */
+                       /* It could probably benefit from a more detailed 
analysis.     */
+                       est_width += num_fields*5 + 8;
+
+                       cache_rows = txfr_size / est_width;           /* (maybe 
1 or 0) */
+
+                       /* To ensure good performance with large rows (near or 
larger   */
+                       /* than our target transfer size) we set a minimum 
cache size.  */
+                       if (cache_rows < 6)     /* is cache a 'useful' size?    
*/
+                           cache_rows = (cache_rows > 0) ? 6 : 4;
+               }
+                   
+       if (cache_rows > 10000000)      /* keep within Oracle's limits  */
+               cache_rows = 10000000;  /* seems it was ub2 at one time now ub4 
this number is arbitary on my part*/
 
     return cache_rows;
 }
@@ -1697,6 +1729,8 @@
 
 
 
+
+
 static int                     /* --- Setup the row cache for this sth --- */
 sth_set_row_cache(SV *h, imp_sth_t *imp_sth, int max_cache_rows, int 
num_fields, int has_longs)
 {
@@ -1704,57 +1738,75 @@
     D_imp_dbh_from_sth;
     D_imp_drh_from_dbh;
     int num_errors = 0;
-    ub4 cache_mem, cache_rows;
+    ub4 cache_mem=0;
+    ub4 cache_rows=10000;/* set high so memory is the limit */
     sword status;
 
-    /* number of rows to cache */
+    /* reworked this is little so the user can set up his own cache
+      basically if rowcachesize or prefetch_mem is set it uses those values
+      otherwise it does it itself
+      no sure what happens in the last case but I lwft it in for now
+      Also I think in later version of OCI this call does not really do 
anything
+    */
+    
+    /* 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);
-    else                                 imp_sth->cache_rows = 
imp_dbh->RowCacheSize;
-
-    if (imp_sth->cache_rows >= 0) {    /* set cache size by row count  */
-       /* imp_sth->est_width needs to be set */
-       cache_mem  = 0;             /* so memory isn't the limit */
-       cache_rows = calc_cache_rows(imp_sth->cache_rows,
-               (int)num_fields, imp_sth->est_width, has_longs);
-       if (max_cache_rows && cache_rows > (unsigned long) max_cache_rows)
-           cache_rows = max_cache_rows;
-       imp_sth->cache_rows = cache_rows;       /* record updated value */
-
-       OCIAttrSet_log_stat(imp_sth->stmhp, OCI_HTYPE_STMT,
-           &cache_mem,  sizeof(cache_mem), OCI_ATTR_PREFETCH_MEMORY,
-           imp_sth->errhp, status);
-       OCIAttrSet_log_stat(imp_sth->stmhp, OCI_HTYPE_STMT,
-               &cache_rows, sizeof(cache_rows), OCI_ATTR_PREFETCH_ROWS,
-               imp_sth->errhp, status);
-       if (status != OCI_SUCCESS) {
-           oci_error(h, imp_sth->errhp, status, "OCIAttrSet 
OCI_ATTR_PREFETCH_ROWS");
-           ++num_errors;
-       }
+  
+   
+       if (imp_dbh->RowCacheSize || imp_sth->prefetch_memory){
+       /*user set values */    
+                cache_rows  =imp_dbh->RowCacheSize;
+            cache_mem   =imp_sth->prefetch_memory;
+            
+       } else if (imp_sth->cache_rows >= 0) {  /* set cache size by row count  
*/
+
+               /* imp_sth->est_width needs to be set */
+               cache_mem  = 0;             /* so memory isn't the limit */
+       
+               cache_rows = 
calc_cache_rows(imp_sth->cache_rows,(int)num_fields, imp_sth->est_width, 
has_longs);
+               
+               if (max_cache_rows && cache_rows > (unsigned long) 
max_cache_rows)
+                   cache_rows = max_cache_rows;
+       
+               imp_sth->cache_rows = cache_rows;       /* record updated value 
*/
+               
     }
     else {                             /* set cache size by memory     */
-       cache_mem  = -imp_sth->cache_rows; /* cache_mem always +ve here */
-       cache_rows = 100000;    /* set high so memory is the limit */
-       if (max_cache_rows &&  cache_rows > (unsigned long) max_cache_rows) {
-           cache_rows = max_cache_rows;
-           imp_sth->cache_rows = cache_rows;   /* record updated value only if 
max_cache_rows */
+                                       /* not sure if we ever reach this*/
+               cache_mem  = -imp_sth->cache_rows; /* cache_mem always +ve here 
*/
+               if (max_cache_rows &&  cache_rows > (unsigned long) 
max_cache_rows) {
+                   cache_rows = max_cache_rows;
+                   imp_sth->cache_rows = cache_rows;   /* record updated value 
only if max_cache_rows */
+               }
+               
+    }
+    
+    
+    OCIAttrSet_log_stat(imp_sth->stmhp, OCI_HTYPE_STMT,
+                                   &cache_mem,  sizeof(cache_mem), 
OCI_ATTR_PREFETCH_MEMORY,
+                                   imp_sth->errhp, status);
+                                   
+       if (status != OCI_SUCCESS) {
+               oci_error(h, imp_sth->errhp, status,
+                               "OCIAttrSet 
OCI_ATTR_PREFETCH_ROWS/OCI_ATTR_PREFETCH_MEMORY");
+               ++num_errors;
        }
+       
        OCIAttrSet_log_stat(imp_sth->stmhp, OCI_HTYPE_STMT,
-           &cache_rows, sizeof(cache_rows), OCI_ATTR_PREFETCH_ROWS,
-           imp_sth->errhp, status);
-       OCIAttrSet_log_stat(imp_sth->stmhp, OCI_HTYPE_STMT,
-           &cache_mem,  sizeof(cache_mem), OCI_ATTR_PREFETCH_MEMORY,
-           imp_sth->errhp, status);
-       if (status != OCI_SUCCESS) {
-           oci_error(h, imp_sth->errhp, status,
-               "OCIAttrSet OCI_ATTR_PREFETCH_ROWS/OCI_ATTR_PREFETCH_MEMORY");
+                               &cache_rows, sizeof(cache_rows), 
OCI_ATTR_PREFETCH_ROWS,
+                       imp_sth->errhp, status);
+                       
+       if (status != OCI_SUCCESS) {
+               oci_error(h, imp_sth->errhp, status, "OCIAttrSet 
OCI_ATTR_PREFETCH_ROWS");
            ++num_errors;
        }
-    }
+       
     if (DBIS->debug >= 3)
-       PerlIO_printf(DBILOGFP,
+               PerlIO_printf(DBILOGFP,
            "    row cache OCI_ATTR_PREFETCH_ROWS %lu, OCI_ATTR_PREFETCH_MEMORY 
%lu\n",
            (unsigned long) (cache_rows), (unsigned long) (cache_mem));
+           
     return num_errors;
 }
 
@@ -2230,9 +2282,12 @@
            fbh->disize += 1;   /* allow for null terminator */
 
        /* dbsize can be zero for 'select NULL ...'                     */
+       
        imp_sth->t_dbsize += fbh->dbsize;
+       
        if (!avg_width)
            avg_width = fbh->dbsize;
+           
        est_width += avg_width;
 
        if (DBIS->debug >= 2)
@@ -2242,9 +2297,8 @@
     imp_sth->est_width = est_width;
 
     sth_set_row_cache(h, imp_sth,
-       (nested_cursors) ? imp_dbh->max_nested_cursors / nested_cursors : 0,
-       (int)num_fields, has_longs
-    );
+               (nested_cursors) ? imp_dbh->max_nested_cursors / nested_cursors 
: 0,
+               (int)num_fields, has_longs );
 
     /* Initialise cache counters */
     imp_sth->in_cache  = 0;
@@ -2371,8 +2425,24 @@
        if (DBIS->debug >= 3){
                PerlIO_printf(DBILOGFP, "    dbd_st_fetch %d fields...\n", 
DBIc_NUM_FIELDS(imp_sth));
            }
+        if (imp_sth->fetch_orient) {
+                       if (imp_sth->exe_mode!=OCI_STMT_SCROLLABLE_READONLY)
+                               croak ("attempt to use a scrollable cursor 
without first setting ora_exe_mode to OCI_STMT_SCROLLABLE_READONLY\n") ;
+
+                       if (DBIS->debug >= 4)
+                               PerlIO_printf(DBILOGFP,"    Scrolling Fetch, 
postion before fetch=%d, Orientation = %s , Fetchoffset =%d\n",
+                                       
imp_sth->fetch_position,oci_fetch_options(imp_sth->fetch_orient),imp_sth->fetch_offset);
+
+                       OCIStmtFetch_log_stat(imp_sth->stmhp, imp_sth->errhp,1, 
imp_sth->fetch_orient,imp_sth->fetch_offset, status);
+                       OCIAttrGet_stmhp_stat(imp_sth, 
&imp_sth->fetch_position, 0, OCI_ATTR_CURRENT_POSITION, status);
+
+                       if (DBIS->debug >= 4)
+                               PerlIO_printf(DBILOGFP,"    Scrolling Fetch, 
postion after fetch=%d\n",imp_sth->fetch_position);
+                               
+               } else {
+                       OCIStmtFetch_log_stat(imp_sth->stmhp, imp_sth->errhp,1, 
(ub2)OCI_FETCH_NEXT, 0, status);
 
-               OCIStmtFetch_log_stat(imp_sth->stmhp, imp_sth->errhp,1, 
(ub2)OCI_FETCH_NEXT, OCI_DEFAULT, status);
+               }
 
     }
 
@@ -2969,10 +3039,9 @@
                return oci_error(sth, errhp, status, "OCIAttrGet OCI_ATTR_ROWID 
/LOB refetch");
 
 
-
-    OCIStmtExecute_log_stat(imp_sth->svchp, lr->stmthp, errhp,
+               OCIStmtExecute_log_stat(imp_sth->svchp, lr->stmthp, errhp,
                1, 0, NULL, NULL, OCI_DEFAULT, status); /* execute and fetch */
-    if (status != OCI_SUCCESS)
+       if (status != OCI_SUCCESS)
        return oci_error(sth, errhp, status,
                ora_sql_error(imp_sth,"OCIStmtExecute/LOB refetch"));
 

Modified: dbd-oracle/trunk/ocitrace.h
==============================================================================
--- dbd-oracle/trunk/ocitrace.h (original)
+++ dbd-oracle/trunk/ocitrace.h Wed Mar 19 08:27:23 2008
@@ -440,18 +440,18 @@
          ul_t((ro)),(void*)(si),(void*)(so),ul_t((md)),                \
          oci_status_name(stat)),stat : stat
 #if !defined(USE_ORA_OCI_STMNT_FETCH)
- #define OCIStmtFetch_log_stat(sh,eh,nr,or,md,stat)                     \
-         stat=OCIStmtFetch2(sh,eh,nr,or,0,md);                                \
+ #define OCIStmtFetch_log_stat(sh,eh,nr,or,os,stat)                     \
+         stat=OCIStmtFetch2(sh,eh,nr,or,os,OCI_DEFAULT);                       
         \
          (DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,                    
    \
            "%sStmtFetch(%p,%p,%lu,%u,%lu)=%s\n",                               
 \
-           OciTp, (void*)sh,(void*)eh,ul_t(nr),(ub2)or,ul_t(md),               
 \
+           OciTp, (void*)sh,(void*)eh,ul_t(nr),(ub2)or,(ub2)os,                
\
            oci_status_name(stat)),stat : stat
 #else
-#define OCIStmtFetch_log_stat(sh,eh,nr,or,md,stat)                     \
-        stat=OCIStmtFetch(sh,eh,nr,or,md);                                \
+#define OCIStmtFetch_log_stat(sh,eh,nr,or,os,stat)                     \
+        stat=OCIStmtFetch(sh,eh,nr,or,OCI_DEFAULT);                            
    \
         (DBD_OCI_TRACEON) ? PerlIO_printf(DBD_OCI_TRACEFP,                     
   \
-          "%sStmtFetch(%p,%p,%lu,%u,%lu)=%s\n",                                
\
-          OciTp, (void*)sh,(void*)eh,ul_t(nr),(ub2)or,ul_t(md),                
\
+          "%sStmtFetch(%p,%p,%lu,%lu)=%s\n",                                \
+          OciTp, (void*)sh,(void*)eh,ul_t(nr),(ub2)or,                \
           oci_status_name(stat)),stat : stat
 #endif
 

Added: dbd-oracle/trunk/t/51scroll.t
==============================================================================
--- (empty file)
+++ dbd-oracle/trunk/t/51scroll.t       Wed Mar 19 08:27:23 2008
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+
+use strict;
+use Test::More tests => 33;
+use DBD::Oracle qw(:ora_types :ora_fetch_orient :ora_exe_modes);
+use DBI;
+
+unshift @INC ,'t';
+require 'nchar_test_lib.pl';
+
+## ----------------------------------------------------------------------------
+## 51scroll.t
+## By John Scoles, The Pythian Group
+## ----------------------------------------------------------------------------
+##  Just a few checks to see if one can use a scrolling cursor
+##  Nothing fancy. 
+## ----------------------------------------------------------------------------
+
+BEGIN {
+       use_ok('DBI');
+}
+
+# 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 });
+ok ($dbh->{RowCacheSize} = 10);
+
+# check that our db handle is good
+isa_ok($dbh, "DBI::db");
+
+my $table = table();
+
+
+$dbh->do(qq{
+       CREATE TABLE $table (
+           id INTEGER )
+    });
+
+
+my ($sql, $sth,$value);
+my $i=0;
+$sql = "INSERT INTO ".$table." VALUES (?)";
+
+$sth =$dbh-> prepare($sql);
+
+for ($i=1;$i<=10;$i++){
+   $sth-> bind_param(1, $i);
+   $sth->execute();
+}
+
+$sql="select * from ".$table;
+ok($sth=$dbh->prepare($sql,{ora_exe_mode=>OCI_STMT_SCROLLABLE_READONLY,ora_prefetch_memory=>200}));
+ok ($sth->execute());
+
+#first loop all the way forward with OCI_FETCH_NEXT
+for($i=1;$i<=10;$i++){
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_NEXT,0);
+   cmp_ok($value->[0], '==', $i, '... we should get the next record');
+}
+
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_CURRENT,0);
+cmp_ok($value->[0], '==', 10, '... we should get the 10th record');
+ 
+#now loop all the way back
+for($i=1;$i<=9;$i++){
+   $value =  $sth->ora_fetch_scroll(OCI_FETCH_PRIOR,0);
+   cmp_ok($value->[0], '==', 10-$i, '... we should get the prior record');
+}
+
+#now +4 records relative from the present position of 0;
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_RELATIVE,4);
+cmp_ok($value->[0], '==', 5, '... we should get the 5th record');
+
+#now +2 records relative from the present position of 4;
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_RELATIVE,2);
+cmp_ok($value->[0], '==', 7, '... we should get the 7th record');
+
+#now -3 records relative from the present position of 6;
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_RELATIVE,-3);
+ 
+cmp_ok($value->[0], '==', 4, '... we should get the 4th record');
+
+#now get the 9th record from the start
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_ABSOLUTE,9);
+ 
+cmp_ok($value->[0], '==', 9, '... we should get the 9th record');
+ 
+#now get the last record
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_LAST,0);
+
+cmp_ok($value->[0], '==', 10, '... we should get the 10th record');
+
+#now get the ora_scroll_position
+
+cmp_ok($sth->ora_scroll_position(), '==', 10, '... we should get the 10 for 
the ora_scroll_position');
+
+#now back to the first
+
+$value =  $sth->ora_fetch_scroll(OCI_FETCH_FIRST,0);
+cmp_ok($value->[0], '==', 1, '... we should get the 1st record');
+
+#check the ora_scroll_position one more time
+
+cmp_ok($sth->ora_scroll_position(), '==', 1, '... we should get the 1 for the 
ora_scroll_position');
+
+$sth->finish();
+drop_table($dbh);
+
+
+$dbh->disconnect;
+
+1;
+

Reply via email to