[ruby-oci8-commit] [343] trunk/ruby-oci8: * ext/oci8/error.c, ext/oci8/extconf.rb, ext/oci8/ oci8.h:
nobody at rubyforge.org
nobody at rubyforge.org
Sun May 17 06:08:42 EDT 2009
Revision: 343
Author: kubo
Date: 2009-05-17 06:08:39 -0400 (Sun, 17 May 2009)
Log Message:
-----------
* ext/oci8/error.c, ext/oci8/extconf.rb, ext/oci8/oci8.h:
fix the native library name in error messages from oci8lib.so
to oci8lib_18.so or oci8lib_191.so.
* ext/oci8/ocinumber.c, test/test_oranumber.rb: fix OraNumber.new
to accept BigDecimal and Rational.
* ext/oci8/bind.c, test/test_oci8.rb: implicitly convert the
specified number to Float when the bind handle is for Float.
* ext/oci8/attr.c: fix for Oracle client which doesn't have
OCIRowidToChar().
* ext/oci8/oci8.c, test/test_appinfo.rb, test/test_all.rb, dist-files:
add new methods OCI8#ping and OCI8#client_identifier=.
add test cases.
* ext/oci8/oci8lib.c: fix a bug for Oracle 8.0.
* lib/oci8/oci8.rb: add a convenient method OCI8#select_one
to fetch one row.
Modified Paths:
--------------
trunk/ruby-oci8/ChangeLog
trunk/ruby-oci8/dist-files
trunk/ruby-oci8/ext/oci8/attr.c
trunk/ruby-oci8/ext/oci8/bind.c
trunk/ruby-oci8/ext/oci8/error.c
trunk/ruby-oci8/ext/oci8/extconf.rb
trunk/ruby-oci8/ext/oci8/oci8.c
trunk/ruby-oci8/ext/oci8/oci8.h
trunk/ruby-oci8/ext/oci8/oci8lib.c
trunk/ruby-oci8/ext/oci8/ocinumber.c
trunk/ruby-oci8/lib/oci8/oci8.rb
trunk/ruby-oci8/test/test_all.rb
trunk/ruby-oci8/test/test_oci8.rb
trunk/ruby-oci8/test/test_oranumber.rb
Added Paths:
-----------
trunk/ruby-oci8/test/test_appinfo.rb
Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ChangeLog 2009-05-17 10:08:39 UTC (rev 343)
@@ -1,3 +1,20 @@
+2009-05-17 KUBO Takehiro <kubo at jiubao.org>
+ * ext/oci8/error.c, ext/oci8/extconf.rb, ext/oci8/oci8.h:
+ fix the native library name in error messages from oci8lib.so
+ to oci8lib_18.so or oci8lib_191.so.
+ * ext/oci8/ocinumber.c, test/test_oranumber.rb: fix OraNumber.new
+ to accept BigDecimal and Rational.
+ * ext/oci8/bind.c, test/test_oci8.rb: implicitly convert the
+ specified number to Float when the bind handle is for Float.
+ * ext/oci8/attr.c: fix for Oracle client which doesn't have
+ OCIRowidToChar().
+ * ext/oci8/oci8.c, test/test_appinfo.rb, test/test_all.rb, dist-files:
+ add new methods OCI8#ping and OCI8#client_identifier=.
+ add test cases.
+ * ext/oci8/oci8lib.c: fix a bug for Oracle 8.0.
+ * lib/oci8/oci8.rb: add a convenient method OCI8#select_one
+ to fetch one row.
+
2009-04-18 KUBO Takehiro <kubo at jiubao.org>
* lib/oci8/oci8.rb: move OCI8::BindType module to bindtype.rb.
* lib/oci8/bindtype.rb: added. This defines OCI8::BindType.
Modified: trunk/ruby-oci8/dist-files
===================================================================
--- trunk/ruby-oci8/dist-files 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/dist-files 2009-05-17 10:08:39 UTC (rev 343)
@@ -54,6 +54,7 @@
test/README
test/config.rb
test/test_all.rb
+test/test_appinfo.rb
test/test_array_dml.rb
test/test_bind_raw.rb
test/test_bind_time.rb
Modified: trunk/ruby-oci8/ext/oci8/attr.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/attr.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/attr.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -106,7 +106,8 @@
* Oracle Server.
*/
oci8_base_t *svc;
- oci8_exec_sql_var_t bind_vars[2];
+ oci8_exec_sql_var_t define_var;
+ oci8_exec_sql_var_t bind_var;
/* search a connection from the handle */
svc = base;
@@ -117,19 +118,19 @@
}
}
/* :strval */
- bind_vars[0].valuep = buf;
- bind_vars[0].value_sz = sizeof(buf);
- bind_vars[0].dty = SQLT_CHR;
- bind_vars[0].indp = NULL;
- bind_vars[0].alenp = &buflen;
+ define_var.valuep = buf;
+ define_var.value_sz = sizeof(buf);
+ define_var.dty = SQLT_CHR;
+ define_var.indp = NULL;
+ define_var.alenp = &buflen;
/* :rowid */
- bind_vars[1].valuep = &arg->ridp;
- bind_vars[1].value_sz = sizeof(void *);
- bind_vars[1].dty = SQLT_RDD;
- bind_vars[1].indp = NULL;
- bind_vars[1].alenp = NULL;
+ bind_var.valuep = &arg->ridp;
+ bind_var.value_sz = sizeof(void *);
+ bind_var.dty = SQLT_RDD;
+ bind_var.indp = NULL;
+ bind_var.alenp = NULL;
/* convert the rowid descriptor to a string value by querying Oracle server. */
- oci8_exec_sql((oci8_svcctx_t*)svc, "BEGIN :strval := :rowid; END;", 0, NULL, 2, bind_vars, 1);
+ oci8_exec_sql((oci8_svcctx_t*)svc, "SELECT :rid FROM dual", 1, &define_var, 1, &bind_var, 1);
if (buflen == 0) {
return Qnil;
}
Modified: trunk/ruby-oci8/ext/oci8/bind.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/bind.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/bind.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -284,8 +284,8 @@
static void bind_float_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
{
- Check_Type(val, T_FLOAT);
- *(double*)data = RFLOAT_VALUE(val);
+ /* val is converted to Float if it isn't Float. */
+ *(double*)data = RFLOAT_VALUE(rb_Float(val));
}
static void bind_float_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE length)
Modified: trunk/ruby-oci8/ext/oci8/error.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/error.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/error.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -157,7 +157,7 @@
#endif
backtrace = rb_funcall(rb_cObject, oci8_id_caller, 0);
if (TYPE(backtrace) == T_ARRAY) {
- snprintf(errmsg, sizeof(errmsg), "%s:%d:in oci8lib.so", file, line);
+ snprintf(errmsg, sizeof(errmsg), "%s:%d:in " STRINGIZE(oci8lib) DLEXT, file, line);
errmsg[sizeof(errmsg) - 1] = '\0';
rb_ary_unshift(backtrace, rb_usascii_str_new_cstr(errmsg));
rb_funcall(exc, oci8_id_set_backtrace, 1, backtrace);
Modified: trunk/ruby-oci8/ext/oci8/extconf.rb
===================================================================
--- trunk/ruby-oci8/ext/oci8/extconf.rb 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/extconf.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -127,6 +127,7 @@
raise 'unsupported ruby version: ' + RUBY_VERSION
end
$defs << "-DInit_oci8lib=Init_#{so_basename}"
+$defs << "-Doci8lib=#{so_basename}"
create_header()
Modified: trunk/ruby-oci8/ext/oci8/oci8.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/oci8.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -17,6 +17,10 @@
#endif
#endif
+#ifndef OCI_ATTR_CLIENT_IDENTIFIER
+#define OCI_ATTR_CLIENT_IDENTIFIER 278
+#endif
+
/*
* Document-class: OCI8
*
@@ -556,6 +560,83 @@
}
}
+/*
+ * call-seq:
+ * ping -> true or false
+ *
+ * Verifies that the Oracle connection is alive.
+ *
+ * OCI8#ping also can be used to flush all the pending OCI client-side calls
+ * to the server if any exist. See: OCI8#client_identifier=.
+ *
+ * For Oracle 10.2 client or upper, a dummy round trip call is made by a newly
+ * added OCI function in Oracle 10.2.
+ *
+ * For Oracle 10.1 client or lower, a simple PL/SQL block "BEGIN NULL; END;"
+ * is executed to make a round trip call.
+ */
+static VALUE oci8_ping(VALUE self)
+{
+ oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
+ if (have_OCIPing_nb) {
+ /* Oracle 10.2 or upper */
+ oci_lc(OCIPing_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, OCI_DEFAULT));
+ } else {
+ /* Oracle 10.1 or lower */
+ oci8_exec_sql(svcctx, "BEGIN NULL; END;", 0U, NULL, 0U, NULL, 1);
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * client_identifier = string
+ *
+ * Sets the CLIENT_IDENTIFIER column in the V$SESSION dictionary view.
+ * This can be up to 64 bytes long. The first character should not be ':'.
+ *
+ * If the Oracle client is 9i or upper, the change is not reflected to the
+ * server immediately. It is postponed until the next round trip call for
+ * example OCI8#exec, OCI8#ping, etc.
+ *
+ * This call is equivalent to "DBMS_SESSION.SET_IDENTIFIER(client_id VARCHAR2);"
+ * and available when the server is Oracle 9i or upper.
+ */
+static VALUE oci8_set_client_identifier(VALUE self, VALUE val)
+{
+ OCISession *sess = oci8_get_oci_session(self);
+ char *ptr;
+ ub4 size;
+
+ if (!NIL_P(val)) {
+ OCI8SafeStringValue(val);
+ ptr = RSTRING_PTR(val);
+ size = RSTRING_LEN(val);
+ } else {
+ ptr = "";
+ size = 0;
+ }
+ if (oracle_client_version >= 900) {
+ if (size > 0 && ptr[0] == ':') {
+ rb_raise(rb_eArgError, "client identifier should not start with ':'.");
+ }
+ oci_lc(OCIAttrSet(sess, OCI_HTYPE_SESSION, ptr,
+ size, OCI_ATTR_CLIENT_IDENTIFIER, oci8_errhp));
+ } else {
+ oci8_exec_sql_var_t bind_vars[1];
+
+ /* :client_id */
+ bind_vars[0].valuep = ptr;
+ bind_vars[0].value_sz = size;
+ bind_vars[0].dty = SQLT_CHR;
+ bind_vars[0].indp = NULL;
+ bind_vars[0].alenp = NULL;
+
+ oci8_exec_sql(oci8_get_svcctx(self), "BEGIN DBMS_SESSION.SET_IDENTIFIER(:client_id); END;", 0, NULL, 1, bind_vars, 1);
+ }
+ return val;
+}
+
VALUE Init_oci8(void)
{
cOCI8 = oci8_define_class("OCI8", &oci8_svcctx_class);
@@ -589,6 +670,8 @@
rb_define_method(cOCI8, "break", oci8_break, 0);
rb_define_method(cOCI8, "prefetch_rows=", oci8_set_prefetch_rows, 1);
rb_define_private_method(cOCI8, "oracle_server_vernum", oci8_oracle_server_vernum, 0);
+ rb_define_method(cOCI8, "ping", oci8_ping, 0);
+ rb_define_method(cOCI8, "client_identifier=", oci8_set_client_identifier, 1);
return cOCI8;
}
Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/oci8.h 2009-05-17 10:08:39 UTC (rev 343)
@@ -117,6 +117,9 @@
#ifndef RFLOAT_VALUE
#define RFLOAT_VALUE(obj) RFLOAT(obj)->value
#endif
+#ifndef STRINGIZE
+#define STRINGIZE(name) #name
+#endif
/* new functions in ruby 1.9.
* define compatible macros for ruby 1.8 or lower.
Modified: trunk/ruby-oci8/ext/oci8/oci8lib.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8lib.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/oci8lib.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -168,7 +168,7 @@
if (rv != OCI_SUCCESS) {
oci8_raise_init_error();
}
- rv = OCIEnvInit(&oci8_global_envhp, OCI_DEFAULT, 0, NULL);
+ rv = OCIEnvInit(&envhp, OCI_DEFAULT, 0, NULL);
if (rv != OCI_SUCCESS) {
oci8_raise_init_error();
}
Modified: trunk/ruby-oci8/ext/oci8/ocinumber.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/ocinumber.c 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/ext/oci8/ocinumber.c 2009-05-17 10:08:39 UTC (rev 343)
@@ -17,6 +17,10 @@
static ID id_power; /* rb_intern("**") */
static ID id_cmp; /* rb_intern("<=>") */
+static ID id_finite_p;
+static ID id_split;
+static ID id_numerator;
+static ID id_denominator;
static VALUE cOCINumber;
static OCINumber const_p1; /* +1 */
@@ -147,6 +151,9 @@
if (!RTEST(rb_obj_is_kind_of(num, rb_cNumeric)))
rb_raise(rb_eTypeError, "expect Numeric but %s", rb_class2name(CLASS_OF(num)));
+ if (rb_respond_to(num, id_finite_p) && !RTEST(rb_funcall(num, id_finite_p, 0))) {
+ rb_raise(rb_eTypeError, "cannot accept number which isn't finite.");
+ }
switch (rb_type(num)) {
case T_FIXNUM:
/* set from long. */
@@ -169,6 +176,74 @@
oci_lc(OCINumberAssign(errhp, DATA_PTR(num), result));
return 1;
}
+ if (rb_respond_to(num, id_split)) {
+ /* BigDecimal */
+ VALUE split = rb_funcall(num, id_split, 0);
+
+ if (TYPE(split) == T_ARRAY && RARRAY_LEN(split) == 4) {
+ /*
+ * sign, significant_digits, base, exponent = num.split
+ * onum = sign * "0.#{significant_digits}".to_f * (base ** exponent)
+ */
+ VALUE *ary = RARRAY_PTR(split);
+ int sign;
+ OCINumber digits;
+ int exponent;
+ int digits_len;
+ OCINumber work;
+
+ /* check sign */
+ if (TYPE(ary[0]) != T_FIXNUM) {
+ goto is_not_big_decimal;
+ }
+ sign = FIX2INT(ary[0]);
+ /* check digits */
+ StringValue(ary[1]);
+ digits_len = RSTRING_LEN(ary[1]);
+ set_oci_number_from_str(&digits, ary[1], Qnil, Qnil, errhp);
+ /* check base */
+ if (TYPE(ary[2]) != T_FIXNUM || FIX2LONG(ary[2]) != 10) {
+ goto is_not_big_decimal;
+ }
+ /* check exponent */
+ if (TYPE(ary[3]) != T_FIXNUM) {
+ goto is_not_big_decimal;
+ }
+ exponent = FIX2INT(ary[3]);
+
+ if (have_OCINumberShift) {
+ /* Oracle 8.1 or upper */
+ oci_lc(OCINumberShift(errhp, &digits, exponent - digits_len, &work));
+ } else {
+ /* Oracle 8.0 */
+ int n = 10;
+ OCINumber base;
+ OCINumber exp;
+
+ oci_lc(OCINumberFromInt(errhp, &n, sizeof(n), OCI_NUMBER_SIGNED, &base));
+ oci_lc(OCINumberIntPower(errhp, &base, exponent - digits_len, &exp));
+ oci_lc(OCINumberMul(errhp, &digits, &exp, &work));
+ }
+ if (sign >= 0) {
+ oci_lc(OCINumberAssign(errhp, &work, result));
+ } else {
+ oci_lc(OCINumberNeg(errhp, &work, result));
+ }
+ return 1;
+ }
+ }
+is_not_big_decimal:
+ if (rb_respond_to(num, id_numerator) && rb_respond_to(num, id_denominator)) {
+ /* Rational */
+ OCINumber numerator;
+ OCINumber denominator;
+
+ if (set_oci_number_from_num(&numerator, rb_funcall(num, id_numerator, 0), 0, errhp) &&
+ set_oci_number_from_num(&denominator, rb_funcall(num, id_denominator, 0), 0, errhp)) {
+ oci_lc(OCINumberDiv(errhp, &numerator, &denominator, result));
+ return 1;
+ }
+ }
if (force) {
/* change via string as a last resort. */
/* TODO: if error, raise TypeError instead of OCI::Error */
@@ -1139,6 +1214,10 @@
id_power = rb_intern("**");
id_cmp = rb_intern("<=>");
+ id_finite_p = rb_intern("finite?");
+ id_split = rb_intern("split");
+ id_numerator = rb_intern("numerator");
+ id_denominator = rb_intern("denominator");
cOCINumber = rb_define_class("OraNumber", rb_cNumeric);
mMath = rb_define_module_under(cOCI8, "Math");
Modified: trunk/ruby-oci8/lib/oci8/oci8.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/oci8.rb 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/lib/oci8/oci8.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -129,6 +129,20 @@
end
end # exec
+ # :call-seq:
+ # select_one(sql, *bindvars) -> first_one_row
+ #
+ def select_one(sql, *bindvars)
+ cursor = self.parse(sql)
+ begin
+ cursor.exec(*bindvars)
+ row = cursor.fetch
+ ensure
+ cursor.close
+ end
+ return row
+ end
+
def username
@username || begin
exec('select user from dual') do |row|
Modified: trunk/ruby-oci8/test/test_all.rb
===================================================================
--- trunk/ruby-oci8/test/test_all.rb 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/test/test_all.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -19,6 +19,7 @@
require "#{srcdir}/test_metadata"
require "#{srcdir}/test_array_dml"
require "#{srcdir}/test_rowid"
+require "#{srcdir}/test_appinfo"
require "#{srcdir}/test_oracle_version"
if OCI8.respond_to? :encoding
Added: trunk/ruby-oci8/test/test_appinfo.rb
===================================================================
--- trunk/ruby-oci8/test/test_appinfo.rb (rev 0)
+++ trunk/ruby-oci8/test/test_appinfo.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -0,0 +1,29 @@
+require 'oci8'
+require 'test/unit'
+require File.dirname(__FILE__) + '/config'
+
+class TestAppInfo < Test::Unit::TestCase
+
+ def setup
+ @conn = get_oci8_connection
+ end
+
+ def test_set_client_identifier
+ # set client_id
+ client_id = "ruby-oci8:#{Process.pid()}"
+ @conn.client_identifier = client_id
+ assert_equal(client_id, @conn.select_one("SELECT SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') FROM DUAL")[0]);
+ # check the first character
+ assert_raise ArgumentError do
+ @conn.client_identifier = ':bad_identifier'
+ end
+
+ # clear client_id
+ @conn.client_identifier = nil
+ assert_nil(@conn.select_one("SELECT SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') FROM DUAL")[0]);
+ end
+
+ def teardown
+ @conn.logoff
+ end
+end
Modified: trunk/ruby-oci8/test/test_oci8.rb
===================================================================
--- trunk/ruby-oci8/test/test_oci8.rb 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/test/test_oci8.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -1,6 +1,8 @@
require 'oci8'
require 'test/unit'
require File.dirname(__FILE__) + '/config'
+require 'bigdecimal'
+require 'rational'
class TestOCI8 < Test::Unit::TestCase
@@ -366,4 +368,39 @@
drop_table('test_table')
end
+ def test_bind_number_with_implicit_conversions
+ src = [1, 1.2, BigDecimal("1.2"), Rational(12, 10)]
+ int = [1, 1, 1, 1]
+ flt = [1, 1.2, 1.2, 1.2]
+
+ cursor = @conn.parse("begin :1 := :2; end;")
+
+ # Float
+ cursor.bind_param(1, nil, Float)
+ cursor.bind_param(2, nil, Float)
+ src.each_with_index do |s, idx|
+ cursor[2] = s
+ cursor.exec
+ assert_equal(cursor[1], flt[idx])
+ end
+
+ # Fixnum
+ cursor.bind_param(1, nil, Fixnum)
+ cursor.bind_param(2, nil, Fixnum)
+ src.each_with_index do |s, idx|
+ cursor[2] = s
+ cursor.exec
+ assert_equal(cursor[1], int[idx])
+ end
+
+ # Integer
+ cursor.bind_param(1, nil, Integer)
+ cursor.bind_param(2, nil, Integer)
+ src.each_with_index do |s, idx|
+ cursor[2] = s
+ cursor.exec
+ assert_equal(cursor[1], int[idx])
+ end
+ end
+
end # TestOCI8
Modified: trunk/ruby-oci8/test/test_oranumber.rb
===================================================================
--- trunk/ruby-oci8/test/test_oranumber.rb 2009-05-08 12:59:02 UTC (rev 342)
+++ trunk/ruby-oci8/test/test_oranumber.rb 2009-05-17 10:08:39 UTC (rev 343)
@@ -3,6 +3,8 @@
require 'test/unit'
require File.dirname(__FILE__) + '/config'
require 'yaml'
+require 'bigdecimal'
+require 'rational'
class TestOraNumber < Test::Unit::TestCase
@@ -480,4 +482,26 @@
assert_equal(expected_val, actual_val, x)
end
end
+
+ def test_new_from_bigdecimal
+ ["+Infinity", "-Infinity", "NaN"].each do |n|
+ assert_raise TypeError do
+ OraNumber.new(BigDecimal.new(n))
+ end
+ end
+
+ LARGE_RANGE_VALUES.each do |val|
+ assert_equal(val, OraNumber.new(BigDecimal.new(val)).to_s)
+ end
+ end
+
+ def test_new_from_rational
+ [
+ [Rational(1, 2), "0.5"],
+ [Rational(3, 5), "0.6"],
+ [Rational(10, 3), "3.3333333333333333333333333333333333333"],
+ ].each do |ary|
+ assert_equal(ary[1], OraNumber.new(ary[0]).to_s)
+ end
+ end
end
More information about the ruby-oci8-commit
mailing list