[ruby-oci8-commit] [532] trunk/ruby-oci8: Refactor OCI8::Cursor to simplify C code.

nobody at rubyforge.org nobody at rubyforge.org
Sat Aug 11 14:22:30 UTC 2012


Revision: 532
Author:   kubo
Date:     2012-08-11 14:22:30 +0000 (Sat, 11 Aug 2012)
Log Message:
-----------
Refactor OCI8::Cursor to simplify C code.

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/attr.c
    trunk/ruby-oci8/ext/oci8/bind.c
    trunk/ruby-oci8/ext/oci8/oci8.h
    trunk/ruby-oci8/ext/oci8/oci8lib.c
    trunk/ruby-oci8/ext/oci8/stmt.c
    trunk/ruby-oci8/lib/oci8/cursor.rb

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ChangeLog	2012-08-11 14:22:30 UTC (rev 532)
@@ -1,4 +1,12 @@
 2012-08-11  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/stmt.c, lib/oci8/cursor.rb: refactor OCI8::Cursor to simplify
+	    C code.
+	* ext/oci8/bind.c: add a private function OCI8::BindType::Base#get_data
+	    and #set_data.
+	* ext/oci8/attr.c, ext/oci8/oci8.h, ext/oci8/oci8lib.c: delete
+	    unused functions and a unused variable.
+
+2012-08-11  KUBO Takehiro  <kubo at jiubao.org>
 	* lib/oci8.rb.in, lib/oci8/cursor.rb, lib/oci8/oci8.rb:
 	    move OCI8::Cursor from lib/oci8/oci8.rb to lib/oci8/cursor.rb.
 

Modified: trunk/ruby-oci8/ext/oci8/attr.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/attr.c	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ext/oci8/attr.c	2012-08-11 14:22:30 UTC (rev 532)
@@ -2,19 +2,10 @@
 /*
  * attr.c
  *
- * Copyright (C) 2002-2007 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2012 KUBO Takehiro <kubo at jiubao.org>
  */
 #include "oci8.h"
 
-VALUE oci8_get_sb1_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp)
-{
-    sb1 val;
-
-    chker3(OCIAttrGet(base->hp.ptr, base->type, &val, NULL, attrtype, oci8_errhp),
-           base, stmtp);
-    return INT2FIX(val);
-}
-
 VALUE oci8_get_ub2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp)
 {
     ub2 val;
@@ -24,38 +15,6 @@
     return INT2FIX(val);
 }
 
-VALUE oci8_get_sb2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp)
-{
-    sb2 val;
-
-    chker3(OCIAttrGet(base->hp.ptr, base->type, &val, NULL, attrtype, oci8_errhp),
-           base, stmtp);
-    return INT2FIX(val);
-}
-
-VALUE oci8_get_ub4_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp)
-{
-    ub4 val;
-
-    chker3(OCIAttrGet(base->hp.ptr, base->type, &val, NULL, attrtype, oci8_errhp),
-           base, stmtp);
-#if SIZEOF_LONG > 4
-    return LONG2FIX(val);
-#else
-    return ULONG2NUM(val);
-#endif
-}
-
-VALUE oci8_get_string_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp)
-{
-    text *val;
-    ub4 size;
-
-    chker3(OCIAttrGet(base->hp.ptr, base->type, &val, &size, attrtype, oci8_errhp),
-           base, stmtp);
-    return rb_external_str_new_with_enc(TO_CHARPTR(val), size, oci8_encoding);
-}
-
 #define MAX_ROWID_LEN 128
 
 typedef struct {

Modified: trunk/ruby-oci8/ext/oci8/bind.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/bind.c	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ext/oci8/bind.c	2012-08-11 14:22:30 UTC (rev 532)
@@ -206,7 +206,7 @@
     return vptr->get(obind, (void*)((size_t)obind->valuep + obind->alloc_sz * idx), null_structp);
 }
 
-VALUE oci8_bind_get_data(VALUE self)
+static VALUE oci8_bind_get_data(VALUE self)
 {
     oci8_bind_t *obind = DATA_PTR(self);
 
@@ -252,7 +252,7 @@
     return self;
 }
 
-void oci8_bind_set_data(VALUE self, VALUE val)
+static VALUE oci8_bind_set_data(VALUE self, VALUE val)
 {
     oci8_bind_t *obind = DATA_PTR(self);
 
@@ -274,6 +274,7 @@
         }
         obind->curar_sz = size;
     }
+    return self;
 }
 
 static VALUE oci8_bind_initialize(VALUE self, VALUE svc, VALUE val, VALUE length, VALUE max_array_size)
@@ -349,6 +350,8 @@
     rb_define_method(cOCI8BindTypeBase, "initialize", oci8_bind_initialize, 4);
     rb_define_method(cOCI8BindTypeBase, "get", oci8_bind_get, 0);
     rb_define_method(cOCI8BindTypeBase, "set", oci8_bind_set, 1);
+    rb_define_private_method(cOCI8BindTypeBase, "get_data", oci8_bind_get_data, 0);
+    rb_define_private_method(cOCI8BindTypeBase, "set_data", oci8_bind_set_data, 1);
 
     /* register primitive data types. */
     oci8_define_bind_class("String", &bind_string_vtable);

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2012-08-11 14:22:30 UTC (rev 532)
@@ -2,7 +2,7 @@
 /*
  * oci8.h - part of ruby-oci8
  *
- * Copyright (C) 2002-2011 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2012 KUBO Takehiro <kubo at jiubao.org>
  */
 #ifndef _RUBY_OCI_H_
 #define _RUBY_OCI_H_ 1
@@ -428,7 +428,6 @@
 extern ID oci8_id_new;
 extern ID oci8_id_get;
 extern ID oci8_id_set;
-extern ID oci8_id_keys;
 extern ID oci8_id_oci8_vtable;
 #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
 extern ID oci8_id_add_op; /* ID of the addition operator '+' */
@@ -496,8 +495,6 @@
 void oci8_bind_hp_obj_mark(oci8_base_t *base);
 void Init_oci8_bind(VALUE cOCI8BindTypeBase);
 oci8_bind_t *oci8_get_bind(VALUE obj);
-void oci8_bind_set_data(VALUE self, VALUE val);
-VALUE oci8_bind_get_data(VALUE self);
 
 /* metadata.c */
 extern VALUE cOCI8MetadataBase;
@@ -551,11 +548,7 @@
 void Init_oci_object(VALUE mOCI);
 
 /* attr.c */
-VALUE oci8_get_sb1_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
 VALUE oci8_get_ub2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
-VALUE oci8_get_sb2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
-VALUE oci8_get_ub4_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
-VALUE oci8_get_string_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
 VALUE oci8_get_rowid_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
 
 /* encoding.c */

Modified: trunk/ruby-oci8/ext/oci8/oci8lib.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8lib.c	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ext/oci8/oci8lib.c	2012-08-11 14:22:30 UTC (rev 532)
@@ -9,7 +9,6 @@
 ID oci8_id_new;
 ID oci8_id_get;
 ID oci8_id_set;
-ID oci8_id_keys;
 ID oci8_id_oci8_vtable;
 #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
 ID oci8_id_add_op;
@@ -80,7 +79,6 @@
     oci8_id_new = rb_intern("new");
     oci8_id_get = rb_intern("get");
     oci8_id_set = rb_intern("set");
-    oci8_id_keys = rb_intern("keys");
     oci8_id_oci8_vtable = rb_intern("__oci8_vtable__");
 #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
     oci8_id_add_op = rb_intern("+");

Modified: trunk/ruby-oci8/ext/oci8/stmt.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/stmt.c	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/ext/oci8/stmt.c	2012-08-11 14:22:30 UTC (rev 532)
@@ -3,29 +3,11 @@
  * stmt.c - part of ruby-oci8
  *         implement the methods of OCIStmt.
  *
- * Copyright (C) 2002-2010 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2012 KUBO Takehiro <kubo at jiubao.org>
  *
  */
 #include "oci8.h"
 
-static VALUE oci8_sym_select_stmt;
-static VALUE oci8_sym_update_stmt;
-static VALUE oci8_sym_delete_stmt;
-static VALUE oci8_sym_insert_stmt;
-static VALUE oci8_sym_create_stmt;
-static VALUE oci8_sym_drop_stmt;
-static VALUE oci8_sym_alter_stmt;
-static VALUE oci8_sym_begin_stmt;
-static VALUE oci8_sym_declare_stmt;
-static ID id_at_column_metadata;
-static ID id_at_actual_array_size;
-static ID id_at_max_array_size;
-static ID id_each_value;
-static ID id_at_names;
-static ID id_empty_p;
-static ID id_at_con;
-static ID id_clear;
-
 VALUE cOCIStmt;
 
 #define TO_STMT(obj) ((oci8_stmt_t *)oci8_get_handle((obj), cOCIStmt))
@@ -33,8 +15,6 @@
 typedef struct {
     oci8_base_t base;
     VALUE svc;
-    VALUE binds;
-    VALUE defns;
     int use_stmt_release;
 } oci8_stmt_t;
 
@@ -42,16 +22,12 @@
 {
     oci8_stmt_t *stmt = (oci8_stmt_t *)base;
     rb_gc_mark(stmt->svc);
-    rb_gc_mark(stmt->binds);
-    rb_gc_mark(stmt->defns);
 }
 
 static void oci8_stmt_free(oci8_base_t *base)
 {
     oci8_stmt_t *stmt = (oci8_stmt_t *)base;
     stmt->svc = Qnil;
-    stmt->binds = Qnil;
-    stmt->defns = Qnil;
     if (stmt->use_stmt_release) {
         OCIStmtRelease(base->hp.stmt, oci8_errhp, NULL, 0, OCI_DEFAULT);
         base->type = 0;
@@ -65,19 +41,15 @@
     sizeof(oci8_stmt_t),
 };
 
-static VALUE oci8_stmt_initialize(int argc, VALUE *argv, VALUE self)
+static VALUE oci8_stmt_initialize(VALUE self, VALUE svc, VALUE sql)
 {
     oci8_stmt_t *stmt = DATA_PTR(self);
     oci8_svcctx_t *svcctx;
-    VALUE svc;
-    VALUE sql;
     sword rv;
 
-    rb_scan_args(argc, argv, "11", &svc, &sql);
-
     svcctx = oci8_get_svcctx(svc);
     oci8_check_pid_consistency(svcctx);
-    if (argc > 1 && oracle_client_version >= ORAVER_9_2) {
+    if (!NIL_P(sql) && oracle_client_version >= ORAVER_9_2) {
         OCI8SafeStringValue(sql);
 
         rv = OCIStmtPrepare2(svcctx->base.hp.svc, &stmt->base.hp.stmt, oci8_errhp, RSTRING_ORATEXT(sql), RSTRING_LEN(sql), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT);
@@ -92,7 +64,7 @@
             oci8_env_raise(oci8_envhp, rv);
         }
         stmt->base.type = OCI_HTYPE_STMT;
-        if (argc > 1) {
+        if (!NIL_P(sql)) {
             OCI8SafeStringValue(sql);
             rv = OCIStmtPrepare(stmt->base.hp.stmt, oci8_errhp, RSTRING_ORATEXT(sql), RSTRING_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
             if (IS_OCI_ERROR(rv)) {
@@ -101,12 +73,6 @@
         }
     }
     stmt->svc = svc;
-    stmt->binds = rb_hash_new();
-    stmt->defns = rb_ary_new();
-    rb_ivar_set(stmt->base.self, id_at_column_metadata, rb_ary_new());
-    rb_ivar_set(stmt->base.self, id_at_names, Qnil);
-    rb_ivar_set(stmt->base.self, id_at_con, svc);
-    rb_ivar_set(stmt->base.self, id_at_max_array_size, Qnil);
 
     oci8_link_to_parent((oci8_base_t*)stmt, (oci8_base_t*)DATA_PTR(svc));
     return Qnil;
@@ -141,13 +107,6 @@
     if (vptr->post_bind_hook != NULL) {
         vptr->post_bind_hook(obind);
     }
-    if (position - 1 < RARRAY_LEN(stmt->defns)) {
-        VALUE old_value = RARRAY_PTR(stmt->defns)[position - 1];
-        if (!NIL_P(old_value)) {
-            oci8_base_free((oci8_base_t*)oci8_get_bind(old_value));
-        }
-    }
-    rb_ary_store(stmt->defns, position - 1, obind->base.self);
     return obind->base.self;
 }
 
@@ -160,7 +119,6 @@
     oci8_bind_t *obind;
     const oci8_bind_vtable_t *vptr;
     sword status;
-    VALUE old_value;
     void *indp;
 
     if (NIL_P(vplaceholder)) { /* 1 */
@@ -207,11 +165,6 @@
     if (vptr->post_bind_hook != NULL) {
         vptr->post_bind_hook(obind);
     }
-    old_value = rb_hash_aref(stmt->binds, vplaceholder);
-    if (!NIL_P(old_value)) {
-        oci8_base_free((oci8_base_t*)oci8_get_bind(old_value));
-    }
-    rb_hash_aset(stmt->binds, vplaceholder, obind->base.self);
     return obind->base.self;
 }
 
@@ -236,55 +189,18 @@
 {
     oci8_stmt_t *stmt = TO_STMT(self);
     oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
-    ub4 iters;
-    ub4 mode;
 
-    if (oci8_get_ub2_attr(&stmt->base, OCI_ATTR_STMT_TYPE, stmt->base.hp.stmt) == INT2FIX(OCI_STMT_SELECT)) {
-        iters = 0;
-        mode = OCI_DEFAULT;
-    } else {
-        if(!NIL_P(iteration_count)) 
-            iters = NUM2INT(iteration_count);
-        else 
-            iters = 1;
-        mode = svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT;
-    }
-    chker3(oci8_call_stmt_execute(svcctx, stmt, iters, mode),
+    chker3(oci8_call_stmt_execute(svcctx, stmt, NUM2UINT(iteration_count),
+                                  svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT),
            &stmt->base, stmt->base.hp.stmt);
     return self;
 }
 
-static VALUE each_value(VALUE obj)
+static VALUE oci8_stmt_fetch(VALUE self, VALUE svc)
 {
-    return rb_funcall(obj, id_each_value, 0);
-}
-
-static VALUE clear_binds_iterator_proc(VALUE val, VALUE arg)
-{
-    if(!NIL_P(val)) {
-        oci8_base_free((oci8_base_t*)oci8_get_bind(val));
-    }
-    return Qnil;
-}
-
-static VALUE oci8_stmt_clear_binds(VALUE self)
-{
     oci8_stmt_t *stmt = TO_STMT(self);
-
-    if(!RTEST(rb_funcall(stmt->binds, id_empty_p, 0)))
-    {
-        rb_iterate(each_value, stmt->binds, clear_binds_iterator_proc, Qnil);
-        rb_funcall(stmt->binds,id_clear,0);
-    }
-
-    return self;
-}
-
-static VALUE oci8_stmt_do_fetch(oci8_stmt_t *stmt, oci8_svcctx_t *svcctx)
-{
-    VALUE ary;
+    oci8_svcctx_t *svcctx = oci8_get_svcctx(svc);
     sword rv;
-    long idx;
     oci8_bind_t *obind;
     const oci8_bind_vtable_t *vptr;
 
@@ -305,43 +221,9 @@
         return Qnil;
     }
     chker3(rv, &svcctx->base, stmt->base.hp.stmt);
-    ary = rb_ary_new2(RARRAY_LEN(stmt->defns));
-    for (idx = 0; idx < RARRAY_LEN(stmt->defns); idx++) {
-        rb_ary_store(ary, idx, oci8_bind_get_data(RARRAY_PTR(stmt->defns)[idx]));
-    }
-    return ary;
+    return self;
 }
 
-/*
- * Gets fetched data as array. This is available for select
- * statement only.
- *
- * example:
- *   conn = OCI8.new('scott', 'tiger')
- *   cursor = conn.exec('SELECT * FROM emp')
- *   while r = cursor.fetch()
- *     puts r.join(',')
- *   end
- *   cursor.close
- *   conn.logoff
- */
-static VALUE oci8_stmt_fetch(VALUE self)
-{
-    oci8_stmt_t *stmt = TO_STMT(self);
-    oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
-
-    if (rb_block_given_p()) {
-        for (;;) {
-            VALUE rs = oci8_stmt_do_fetch(stmt, svcctx);
-            if (NIL_P(rs))
-                return self; /* NEED TO CHECK 0.1 behavior. */
-            rb_yield(rs);
-        }
-    } else {
-        return oci8_stmt_do_fetch(stmt, svcctx);
-    }
-}
-
 static VALUE oci8_stmt_get_param(VALUE self, VALUE pos)
 {
     oci8_stmt_t *stmt = TO_STMT(self);
@@ -357,61 +239,6 @@
 }
 
 /*
- * gets the type of SQL statement as follows.
- * * OCI8::STMT_SELECT
- * * OCI8::STMT_UPDATE
- * * OCI8::STMT_DELETE
- * * OCI8::STMT_INSERT
- * * OCI8::STMT_CREATE
- * * OCI8::STMT_DROP
- * * OCI8::STMT_ALTER
- * * OCI8::STMT_BEGIN (PL/SQL block which starts with a BEGIN keyword)
- * * OCI8::STMT_DECLARE (PL/SQL block which starts with a DECLARE keyword)
- * * Other Fixnum value undocumented in Oracle manuals.
- *
- * <em>Changes between ruby-oci8 1.0 and 2.0.</em>
- *
- * [ruby-oci8 2.0] OCI8::STMT_* are Symbols. (:select_stmt, :update_stmt, etc.)
- * [ruby-oci8 1.0] OCI8::STMT_* are Fixnums. (1, 2, 3, etc.)
- */
-static VALUE oci8_stmt_get_stmt_type(VALUE self)
-{
-    oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
-    VALUE stmt_type = oci8_get_ub2_attr(base, OCI_ATTR_STMT_TYPE, base->hp.stmt);
-    switch (FIX2INT(stmt_type)) {
-    case OCI_STMT_SELECT:
-        return oci8_sym_select_stmt;
-    case OCI_STMT_UPDATE:
-        return oci8_sym_update_stmt;
-    case OCI_STMT_DELETE:
-        return oci8_sym_delete_stmt;
-    case OCI_STMT_INSERT:
-        return oci8_sym_insert_stmt;
-    case OCI_STMT_CREATE:
-        return oci8_sym_create_stmt;
-    case OCI_STMT_DROP:
-        return oci8_sym_drop_stmt;
-    case OCI_STMT_ALTER:
-        return oci8_sym_alter_stmt;
-    case OCI_STMT_BEGIN:
-        return oci8_sym_begin_stmt;
-    case OCI_STMT_DECLARE:
-        return oci8_sym_declare_stmt;
-    default:
-        return stmt_type;
-    }
-}
-
-/*
- * Returns the number of processed rows.
- */
-static VALUE oci8_stmt_get_row_count(VALUE self)
-{
-    oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
-    return oci8_get_ub4_attr(base, OCI_ATTR_ROW_COUNT, base->hp.stmt);
-}
-
-/*
  * Get the rowid of the last inserted/updated/deleted row.
  * This cannot be used for select statements.
  *
@@ -431,159 +258,7 @@
     return oci8_get_rowid_attr(base, OCI_ATTR_ROWID, base->hp.stmt);
 }
 
-static VALUE oci8_stmt_get_param_count(VALUE self)
-{
-    oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
-    return oci8_get_ub4_attr(base, OCI_ATTR_PARAM_COUNT, base->hp.stmt);
-}
-
 /*
- * call-seq:
- *   [key]
- *
- * Gets the value of the bind variable.
- *
- * In case of binding explicitly, use same key with that of
- * OCI8::Cursor#bind_param. A placeholder can be bound by
- * name or position. If you bind by name, use that name. If you bind
- * by position, use the position.
- *
- * example:
- *   cursor = conn.parse("BEGIN :out := 'BAR'; END;")
- *   cursor.bind_param(':out', 'FOO') # bind by name
- *   p cursor[':out'] # => 'FOO'
- *   p cursor[1] # => nil
- *   cursor.exec()
- *   p cursor[':out'] # => 'BAR'
- *   p cursor[1] # => nil
- *
- * example:
- *   cursor = conn.parse("BEGIN :out := 'BAR'; END;")
- *   cursor.bind_param(1, 'FOO') # bind by position
- *   p cursor[':out'] # => nil
- *   p cursor[1] # => 'FOO'
- *   cursor.exec()
- *   p cursor[':out'] # => nil
- *   p cursor[1] # => 'BAR'
- *
- * In case of binding by OCI8#exec or OCI8::Cursor#exec,
- * get the value by position, which starts from 1.
- *
- * example:
- *   cursor = conn.exec("BEGIN :out := 'BAR'; END;", 'FOO')
- *   # 1st bind variable is bound as String with width 3. Its initial value is 'FOO'
- *   # After execute, the value become 'BAR'.
- *   p cursor[1] # => 'BAR'
- */
-static VALUE oci8_stmt_aref(VALUE self, VALUE key)
-{
-    oci8_stmt_t *stmt = TO_STMT(self);
-    VALUE obj = rb_hash_aref(stmt->binds, key);
-    if (NIL_P(obj)) {
-        return Qnil;
-    }
-    return oci8_bind_get_data(obj);
-}
-
-/*
- * call-seq:
- *   [key] = val
- *
- * Sets the value to the bind variable. The way to specify the
- * +key+ is same with OCI8::Cursor#[]. This is available
- * to replace the value and execute many times.
- *
- * example1:
- *   cursor = conn.parse("INSERT INTO test(col1) VALUES(:1)")
- *   cursor.bind_params(1, nil, String, 3)
- *   ['FOO', 'BAR', 'BAZ'].each do |key|
- *     cursor[1] = key
- *     cursor.exec
- *   end
- *   cursor.close()
- *
- * example2:
- *   ['FOO', 'BAR', 'BAZ'].each do |key|
- *     conn.exec("INSERT INTO test(col1) VALUES(:1)", key)
- *   end
- *
- * Both example's results are same. But the former will use less resources.
- */
-static VALUE oci8_stmt_aset(VALUE self, VALUE key, VALUE val)
-{
-    long max_array_size;
-    long actual_array_size;
-    long bind_array_size;
-
-    oci8_stmt_t *stmt = TO_STMT(self);
-    VALUE obj = rb_hash_aref(stmt->binds, key);
-    if (NIL_P(obj)) {
-        return Qnil; /* ?? MUST BE ERROR? */
-    }
-
-    if(TYPE(val) == T_ARRAY) {
-        max_array_size = NUM2INT(rb_ivar_get(self, id_at_max_array_size));
-        actual_array_size = NUM2INT(rb_ivar_get(self, id_at_actual_array_size));
-        bind_array_size = RARRAY_LEN(val);
-
-        if(actual_array_size > 0 && bind_array_size != actual_array_size) {
-            rb_raise(rb_eRuntimeError, "all binding arrays hould be the same size");        
-        }
-        if(bind_array_size <= max_array_size && actual_array_size == 0) {
-            rb_ivar_set(self, id_at_actual_array_size, INT2NUM(bind_array_size));
-        }
-    }
-    oci8_bind_set_data(obj, val);
-    return val;
-}
-
-/*
- * call-seq:
- *   keys -> an Array
- *
- * Returns the keys of bind variables as array.
- */
-static VALUE oci8_stmt_keys(VALUE self)
-{
-    oci8_stmt_t *stmt = TO_STMT(self);
-    return rb_funcall(stmt->binds, oci8_id_keys, 0);
-}
-
-static VALUE oci8_stmt_defined_p(VALUE self, VALUE pos)
-{
-    oci8_stmt_t *stmt = TO_STMT(self);
-    long position = NUM2INT(pos);
-
-    if (position - 1 < RARRAY_LEN(stmt->defns)) {
-        VALUE value = RARRAY_PTR(stmt->defns)[position - 1];
-        if (!NIL_P(value)) {
-            return Qtrue;
-        }
-    }
-    return Qfalse;
-}
-
-/*
- * call-seq:
- *   prefetch_rows = aFixnum
- *
- * Set number of rows to be prefetched.
- * This can reduce the number of network round trips when fetching
- * many rows. The default value is one.
- *
- * FYI: Rails oracle adaptor uses 100 by default.
- */
-static VALUE oci8_stmt_set_prefetch_rows(VALUE self, VALUE rows)
-{
-    oci8_stmt_t *stmt = TO_STMT(self);
-    ub4 num = NUM2UINT(rows);
-
-    chker2(OCIAttrSet(stmt->base.hp.ptr, OCI_HTYPE_STMT, &num, 0, OCI_ATTR_PREFETCH_ROWS, oci8_errhp),
-           &stmt->base);
-    return Qfalse;
-}
-
-/*
  * bind_stmt
  */
 VALUE oci8_stmt_get(oci8_bind_t *obind, void *data, void *null_struct)
@@ -646,40 +321,13 @@
 #endif
     cOCIStmt = oci8_define_class_under(cOCI8, "Cursor", &oci8_stmt_vtable);
 
-    oci8_sym_select_stmt = ID2SYM(rb_intern("select_stmt"));
-    oci8_sym_update_stmt = ID2SYM(rb_intern("update_stmt"));
-    oci8_sym_delete_stmt = ID2SYM(rb_intern("delete_stmt"));
-    oci8_sym_insert_stmt = ID2SYM(rb_intern("insert_stmt"));
-    oci8_sym_create_stmt = ID2SYM(rb_intern("create_stmt"));
-    oci8_sym_drop_stmt = ID2SYM(rb_intern("drop_stmt"));
-    oci8_sym_alter_stmt = ID2SYM(rb_intern("alter_stmt"));
-    oci8_sym_begin_stmt = ID2SYM(rb_intern("begin_stmt"));
-    oci8_sym_declare_stmt = ID2SYM(rb_intern("declare_stmt"));
-    id_at_column_metadata = rb_intern("@column_metadata");
-    id_at_actual_array_size = rb_intern("@actual_array_size");
-    id_at_max_array_size = rb_intern("@max_array_size");
-    id_each_value = rb_intern("each_value");
-    id_at_names = rb_intern("@names");
-    id_at_con = rb_intern("@con");
-    id_empty_p = rb_intern("empty?");
-    id_clear = rb_intern("clear");
-
-    rb_define_private_method(cOCIStmt, "initialize", oci8_stmt_initialize, -1);
+    rb_define_private_method(cOCIStmt, "__initialize", oci8_stmt_initialize, 2);
     rb_define_private_method(cOCIStmt, "__define", oci8_define_by_pos, 2);
     rb_define_private_method(cOCIStmt, "__bind", oci8_bind, 2);
     rb_define_private_method(cOCIStmt, "__execute", oci8_stmt_execute, 1);
-    rb_define_private_method(cOCIStmt, "__clearBinds", oci8_stmt_clear_binds, 0);
-    rb_define_method(cOCIStmt, "fetch", oci8_stmt_fetch, 0);
+    rb_define_private_method(cOCIStmt, "__fetch", oci8_stmt_fetch, 1);
     rb_define_private_method(cOCIStmt, "__paramGet", oci8_stmt_get_param, 1);
-    rb_define_method(cOCIStmt, "type", oci8_stmt_get_stmt_type, 0);
-    rb_define_method(cOCIStmt, "row_count", oci8_stmt_get_row_count, 0);
     rb_define_method(cOCIStmt, "rowid", oci8_stmt_get_rowid, 0);
-    rb_define_private_method(cOCIStmt, "__param_count", oci8_stmt_get_param_count, 0);
-    rb_define_method(cOCIStmt, "[]", oci8_stmt_aref, 1);
-    rb_define_method(cOCIStmt, "[]=", oci8_stmt_aset, 2);
-    rb_define_method(cOCIStmt, "keys", oci8_stmt_keys, 0);
-    rb_define_private_method(cOCIStmt, "__defined?", oci8_stmt_defined_p, 1);
-    rb_define_method(cOCIStmt, "prefetch_rows=", oci8_stmt_set_prefetch_rows, 1);
 
     oci8_define_bind_class("Cursor", &bind_stmt_vtable);
 }

Modified: trunk/ruby-oci8/lib/oci8/cursor.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/cursor.rb	2012-08-11 12:25:05 UTC (rev 531)
+++ trunk/ruby-oci8/lib/oci8/cursor.rb	2012-08-11 14:22:30 UTC (rev 532)
@@ -14,6 +14,16 @@
   # calling OCI8#exec or OCI8#parse.
   class Cursor
 
+    def initialize(con, sql = nil)
+      @bind_handles = {}
+      @define_handles = []
+      @column_metadata = []
+      @names = nil
+      @con = con
+      @max_array_size = nil
+      __initialize(con, sql)
+    end
+
     # explicitly indicate the date type of fetched value. run this
     # method within parse and exec. pos starts from 1. lentgh is used
     # when type is String.
@@ -24,7 +34,12 @@
     #   cursor.define(2, Time)       # fetch the second column as Time.
     #   cursor.exec()
     def define(pos, type, length = nil)
-      __define(pos, make_bind_object(:type => type, :length => length))
+      bindobj = make_bind_object(:type => type, :length => length)
+      __define(pos, bindobj)
+      if old = @define_handles[pos - 1]
+        old.send(:free)
+      end
+      @define_handles[pos - 1] = bindobj
       self
     end # define
 
@@ -77,7 +92,12 @@
       else
         param = {:value => param, :type => type,  :length => length}
       end
-      __bind(key, make_bind_object(param))
+      bindobj = make_bind_object(param)
+      __bind(key, bindobj)
+      if old = @bind_handles[key]
+        old.send(:free)
+      end
+      @bind_handles[key] = bindobj
       self
     end # bind_param
 
@@ -97,15 +117,133 @@
     # variables.
     def exec(*bindvars)
       bind_params(*bindvars)
-      __execute(nil) # Pass a nil to specify the statement isn't an Array DML
       case type
       when :select_stmt
+        __execute(0)
         define_columns()
       else
+        __execute(1)
         row_count
       end
     end # exec
 
+    # Gets fetched data as array. This is available for select
+    # statement only.
+    #
+    # example:
+    #   conn = OCI8.new('scott', 'tiger')
+    #   cursor = conn.exec('SELECT * FROM emp')
+    #   while r = cursor.fetch()
+    #     puts r.join(',')
+    #   end
+    #   cursor.close
+    #   conn.logoff
+    def fetch
+      if block_given?
+        while row = fetch_one_row_as_array
+          yield row
+        end
+        self
+      else
+        fetch_one_row_as_array
+      end
+    end
+
+    # call-seq:
+    #   fetch_hash
+    #
+    # get fetched data as a Hash. The hash keys are column names.
+    # If a block is given, acts as an iterator.
+    def fetch_hash
+      if iterator?
+        while ret = fetch_one_row_as_hash()
+          yield(ret)
+        end
+      else
+        fetch_one_row_as_hash
+      end
+    end
+
+    # call-seq:
+    #   [key]
+    #
+    # Gets the value of the bind variable.
+    #
+    # In case of binding explicitly, use same key with that of
+    # OCI8::Cursor#bind_param. A placeholder can be bound by
+    # name or position. If you bind by name, use that name. If you bind
+    # by position, use the position.
+    #
+    # example:
+    #   cursor = conn.parse("BEGIN :out := 'BAR'; END;")
+    #   cursor.bind_param(':out', 'FOO') # bind by name
+    #   p cursor[':out'] # => 'FOO'
+    #   p cursor[1] # => nil
+    #   cursor.exec()
+    #   p cursor[':out'] # => 'BAR'
+    #   p cursor[1] # => nil
+    #
+    # example:
+    #   cursor = conn.parse("BEGIN :out := 'BAR'; END;")
+    #   cursor.bind_param(1, 'FOO') # bind by position
+    #   p cursor[':out'] # => nil
+    #   p cursor[1] # => 'FOO'
+    #   cursor.exec()
+    #   p cursor[':out'] # => nil
+    #   p cursor[1] # => 'BAR'
+    #
+    # In case of binding by OCI8#exec or OCI8::Cursor#exec,
+    # get the value by position, which starts from 1.
+    #
+    # example:
+    #   cursor = conn.exec("BEGIN :out := 'BAR'; END;", 'FOO')
+    #   # 1st bind variable is bound as String with width 3. Its initial value is 'FOO'
+    #   # After execute, the value become 'BAR'.
+    #   p cursor[1] # => 'BAR'
+    def [](key)
+      handle = @bind_handles[key]
+      handle && handle.send(:get_data)
+    end
+
+    # call-seq:
+    #   [key] = val
+    #
+    # Sets the value to the bind variable. The way to specify the
+    # +key+ is same with OCI8::Cursor#[]. This is available
+    # to replace the value and execute many times.
+    #
+    # example1:
+    #   cursor = conn.parse("INSERT INTO test(col1) VALUES(:1)")
+    #   cursor.bind_params(1, nil, String, 3)
+    #   ['FOO', 'BAR', 'BAZ'].each do |key|
+    #     cursor[1] = key
+    #     cursor.exec
+    #   end
+    #   cursor.close()
+    #
+    # example2:
+    #   ['FOO', 'BAR', 'BAZ'].each do |key|
+    #     conn.exec("INSERT INTO test(col1) VALUES(:1)", key)
+    #   end
+    #
+    # Both example's results are same. But the former will use less resources.
+    #
+    def []=(key, val)
+      handle = @bind_handles[key]
+      return nil if handle.nil?
+
+      if val.is_a? Array
+        if @actual_array_size > 0 && val.length != @actual_array_size
+          raise RuntimeError, "all binding arrays hould be the same size"
+        end
+        if @actual_array_size == 0 && val.length <= @max_array_size
+          @actual_array_size = val.length
+        end
+      end
+      handle.send(:set_data, val)
+      val
+    end
+
     # Set the maximum array size for bind_param_array
     #
     # All the binds will be clean from cursor if instance variable max_array_size is set before
@@ -114,7 +252,7 @@
     #  all the binding arrays are required to be the same size
     def max_array_size=(size)
       raise "expect positive number for max_array_size." if size.nil? && size <=0
-      __clearBinds if !@max_array_size.nil?
+      free_bind_handles()  if !@max_array_size.nil?
       @max_array_size = size
       @actual_array_size = nil
     end # max_array_size=
@@ -163,6 +301,11 @@
       raise "unsupported dataType: #{type}" if bindclass.nil?
       bindobj = bindclass.create(@con, var_array, param, @max_array_size)
       __bind(key, bindobj)
+      #
+      if old = @bind_handles[key]
+        old.send(:free)
+      end
+      @bind_handles[key] = bindobj
       self
     end # bind_param_array
 
@@ -210,21 +353,6 @@
       @column_metadata
     end
 
-    # call-seq:
-    #   fetch_hash
-    #
-    # get fetched data as a Hash. The hash keys are column names.
-    # If a block is given, acts as an iterator.
-    def fetch_hash
-      if iterator?
-        while ret = fetch_a_hash_row()
-          yield(ret)
-        end
-      else
-        fetch_a_hash_row
-      end
-    end # fetch_hash
-
     # close the cursor.
     def close
       free()
@@ -232,6 +360,33 @@
       @column_metadata = nil
     end # close
 
+    # call-seq:
+    #   keys -> an Array
+    #
+    # Returns the keys of bind variables as array.
+    def keys
+      @bind_handles.keys
+    end
+
+    # call-seq:
+    #   prefetch_rows = aFixnum
+    #
+    # Set number of rows to be prefetched.
+    # This can reduce the number of network round trips when fetching
+    # many rows. The default value is one.
+    #
+    # FYI: Rails oracle adaptor uses 100 by default.
+    #
+    def prefetch_rows=(rows)
+      attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
+    end
+
+    # Returns the number of processed rows.
+    def row_count
+      # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5498
+      attr_get_ub4(9) # OCI_ATTR_ROW_COUNT(9)
+    end
+
     # Returns the text of the SQL statement prepared in the cursor.
     #
     # @note
@@ -250,6 +405,50 @@
       attr_get_string(144)
     end
 
+    # gets the type of SQL statement as follows.
+    # * OCI8::STMT_SELECT
+    # * OCI8::STMT_UPDATE
+    # * OCI8::STMT_DELETE
+    # * OCI8::STMT_INSERT
+    # * OCI8::STMT_CREATE
+    # * OCI8::STMT_DROP
+    # * OCI8::STMT_ALTER
+    # * OCI8::STMT_BEGIN (PL/SQL block which starts with a BEGIN keyword)
+    # * OCI8::STMT_DECLARE (PL/SQL block which starts with a DECLARE keyword)
+    # * Other Fixnum value undocumented in Oracle manuals.
+    #
+    # <em>Changes between ruby-oci8 1.0 and 2.0.</em>
+    #
+    # [ruby-oci8 2.0] OCI8::STMT_* are Symbols. (:select_stmt, :update_stmt, etc.)
+    # [ruby-oci8 1.0] OCI8::STMT_* are Fixnums. (1, 2, 3, etc.)
+    #
+    def type
+      # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5506
+      stmt_type = attr_get_ub2(24) # OCI_ATTR_STMT_TYPE(24)
+      case stmt_type
+      when 1 # OCI_STMT_SELECT
+        :select_stmt
+      when 2 # OCI_STMT_UPDATE
+        :update_stmt
+      when 3 # OCI_STMT_DELETE
+        :delete_stmt
+      when 4 # OCI_STMT_INSERT
+        :insert_stmt
+      when 5 # OCI_STMT_CREATE
+        :create_stmt
+      when 6 # OCI_STMT_DROP
+        :drop_stmt
+      when 7 # OCI_STMT_ALTER
+        :alter_stmt
+      when 8 # OCI_STMT_BEGIN
+        :begin_stmt
+      when 9 # OCI_STMT_DECLARE
+        :declare_stmt
+      else
+        stmt_type
+      end
+    end
+
     private
 
     def make_bind_object(param)
@@ -296,40 +495,62 @@
     end
 
     def define_columns
-      num_cols = __param_count
+      # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5494
+      num_cols = attr_get_ub4(18) # OCI_ATTR_PARAM_COUNT(18)
       1.upto(num_cols) do |i|
         parm = __paramGet(i)
-        define_one_column(i, parm) unless __defined?(i)
+        define_one_column(i, parm) unless @define_handles[i - 1]
         @column_metadata[i - 1] = parm
       end
       num_cols
     end # define_columns
 
     def define_one_column(pos, param)
-      __define(pos, make_bind_object(param))
+      bindobj = make_bind_object(param)
+      __define(pos, bindobj)
+      if old = @define_handles[pos - 1]
+        old.send(:free)
+      end
+      @define_handles[pos - 1] = bindobj
     end # define_one_column
 
     def bind_params(*bindvars)
       bindvars.each_with_index do |val, i|
-	if val.is_a? Array
-	  bind_param(i + 1, val[0], val[1], val[2])
-	else
-	  bind_param(i + 1, val)
-	end
+        if val.is_a? Array
+          bind_param(i + 1, val[0], val[1], val[2])
+        else
+          bind_param(i + 1, val)
+        end
       end
     end # bind_params
 
-    def fetch_a_hash_row
-      if rs = fetch()
+    def fetch_one_row_as_array
+      if __fetch(@con)
+        @define_handles.collect do |handle|
+          handle.send(:get_data)
+        end
+      else
+        nil
+      end
+    end
+
+    def fetch_one_row_as_hash
+      if __fetch(@con)
         ret = {}
-        get_col_names.each do |name|
-          ret[name] = rs.shift
+        get_col_names.each_with_index do |name, idx|
+          ret[name] = @define_handles[idx].send(:get_data)
         end
         ret
-      else 
+      else
         nil
       end
-    end # fetch_a_hash_row
+    end
 
+    def free_bind_handles
+      @bind_handles.each_value do |val|
+        val.send(:free)
+      end
+      @bind_handles.clear
+    end
   end
 end



More information about the ruby-oci8-commit mailing list