ruby-oci8 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,83 @@
1
+ 2010-02-28 KUBO Takehiro <kubo@jiubao.org>
2
+ * NEWS: add changes between 2.0.3 and 2.0.4.
3
+ * VERSION: change the version to 2.0.3.
4
+ * ext/oci8/stmt.c: fix segmentation fault when OCI8::Cursor#fetch
5
+ is called prior to OCI8::Cursor#exec.
6
+ * ext/oci8/oci8.c: minor fix in rdoc comment.
7
+
8
+ 2010-02-27 KUBO Takehiro <kubo@jiubao.org>
9
+ * lib/oci8/datetime.rb: fix a problem that fractional seconds are lost
10
+ when Time value is bound to TIMESTAMP.
11
+ (reported by Raimonds Simanovskis)
12
+
13
+ 2010-02-27 KUBO Takehiro <kubo@jiubao.org>
14
+ * ext/oci8/error.c, ext/oci8/extconf.rb, ext/oci8/oci8.h: fix for
15
+ old Oracle versions, which lack declarations of OCIMsg, oraub8,
16
+ orasb8 and OCI_DURATION_PROCESS.
17
+ * ext/oci8/ocihandle.c: fix for mingw compiler.
18
+
19
+ 2010-02-27 KUBO Takehiro <kubo@jiubao.org>
20
+ * ext/oci8/apiwrap.yml, ext/oci8/error.c, ext/oci8/oci8.h,
21
+ ext/oci8/ocinumber.c: fix for Oracle 8.0 client, which doesn't
22
+ have OCIMessageOpen() and OCIMessageGet().
23
+ * ext/oci8/oci8.c: add a new method OCI8.error_message to get
24
+ a error message which depends on NLS_LANGUAGE.
25
+
26
+ 2010-02-09 KUBO Takehiro <kubo@jiubao.org>
27
+ * dist-files: add ext/oci8/oranumber_util.c and
28
+ ext/oci8/oranumber_util.h.
29
+ (reported by Raimonds Simanovskis)
30
+
31
+ 2010-02-07 KUBO Takehiro <kubo@jiubao.org>
32
+ * ext/oci8/ocinumber.c, ext/oci8/oranumber_util.c,
33
+ ext/oci8/oranumber_util.h: change the declaration of
34
+ oranumber_to_str() to prevent buffer overflow by
35
+ unexpected invalid Oracle number internal data.
36
+
37
+ 2010-02-07 KUBO Takehiro <kubo@jiubao.org>
38
+ * ext/oci8/error.c, ext/oci8/oci8.h: add oci8_raise_by_msgno()
39
+ to retrieve a Oracle error message which depends on NLS_LANGUAGE.
40
+ * ext/oci8/oranumber_util.c, ext/oci8/oranumber_util.h,
41
+ ext/oci8/extconf.rb: add handwritten conversion functions from
42
+ OCINumber internal representation to string and vice versa.
43
+ * ext/oci8/ocinumber.c: 1. use handwritten conversion functions
44
+ instead of OCI functions to convert OraNumber to string
45
+ and vice varse. 2. add OraNumber#dump.
46
+ * test/test_oranumber.rb: add test cases to check conversion from
47
+ OraNumber to string and vice varse.
48
+
49
+ 2010-02-02 KUBO Takehiro <kubo@jiubao.org>
50
+ * ext/oci8/ocinumber.c: fix to support NUMBERS with scale larger
51
+ than 38 by using scientific number notation to convert OraNumber
52
+ to BigDecimal. OraNumber#to_i is also fixed.
53
+ (reported by Raimonds Simanovskis)
54
+
55
+ 2010-01-24 KUBO Takehiro <kubo@jiubao.org>
56
+ * ext/oci8/error.c: Use OCIErrorGet() to retrieve the error message
57
+ when the OCI return code is OCI_NO_DATA.
58
+ (reported by Raimonds Simanovskis)
59
+
60
+ 2009-12-06 KUBO Takehiro <kubo@jiubao.org>
61
+ * ext/oci8/object.c: 1. fix segv when GC starts while initializing
62
+ a bind object for object type. (reported by Remi Gagnon)
63
+ 2. fix memory leak about 30 bytes per one bind object for object
64
+ type.
65
+
66
+ 2009-11-03 KUBO Takehiro <kubo@jiubao.org>
67
+ * ext/oci8/object.c: fix segv when binding a collection of string.
68
+ (reported by Raimonds Simanovskis)
69
+
70
+ 2009-10-26 KUBO Takehiro <kubo@jiubao.org>
71
+ * NEW: fix typo.
72
+ * dist-files, ext/oci8/.document: add ocihandle.c.
73
+ * ext/oci8/ocihandle.c: add private methods OCIHandle#attr_get_*,
74
+ OCIHandle#attr_set_* and their rdoc comments.
75
+
76
+ 2009-10-23 KUBO Takehiro <kubo@jiubao.org>
77
+ * ext/oci8/extconf.rb, ext/oci8/oci8.h, ext/oci8/oci8lib.c,
78
+ ext/oci8/ocihandle.c: add ocihandle.c and move OCIHandle
79
+ definitions from oci8lib.c to the file.
80
+
1
81
  2009-10-21 KUBO Takehiro <kubo@jiubao.org>
2
82
  * NEWS: add changes between 2.0.2 and 2.0.3.
3
83
  * VERSION, Makefile: change the version to 2.0.3.
data/NEWS CHANGED
@@ -1,6 +1,61 @@
1
+ 2.0.4:
2
+
3
+ * New Features
4
+
5
+ - OCI8.error_message(message_no) -> string
6
+
7
+ Gets the Oracle error message specified by message id.
8
+ Its language depends on NLS_LANGUAGE.
9
+
10
+ Note: This method is unavailable if the Oracle client
11
+ version is 8.0.
12
+
13
+ # When NLS_LANG is AMERICAN_AMERICA.AL32UTF8
14
+ OCI8.error_message(1)
15
+ # => "ORA-00001: unique constraint (%s.%s) violated"
16
+
17
+ # When NLS_LANG is FRENCH_FRANCE.AL32UTF8
18
+ OCI8.error_message(1)
19
+ # => "ORA-00001: violation de contrainte unique (%s.%s)"
20
+
21
+ - OraNumber#dump -> string
22
+
23
+ Returns OraNumber's internal representation whose format
24
+ is same with the return value of Oracle SQL function DUMP().
25
+
26
+ OraNumber.new(100).dump #=> "Typ=2 Len=2: 194,2"
27
+ OraNumber.new(123).dump #=> "Typ=2 Len=3: 194,2,24"
28
+ OraNumber.new(0.1).dump #=> "Typ=2 Len=2: 192,11"
29
+
30
+ * Fixed issues
31
+
32
+ - Fractional second part is lost when ruby's Time instance is bound
33
+ to Oracle datatype TIMESTAMP.
34
+ (reported by Raimonds Simanovskis)
35
+
36
+ - OraNumber#to_i and OraNumber#to_s fail when its scale is larger
37
+ than 38.
38
+ (reported by Raimonds Simanovskis)
39
+
40
+ - Memory leak about 30 bytes per one place holder for object type.
41
+
42
+ - Segmentation fault when a collection of string is bound.
43
+ (reported by Raimonds Simanovskis)
44
+
45
+ - Segmentation fault when GC starts while initializing a bind
46
+ object for object type.
47
+ (reported by Remi Gagnon)
48
+
49
+ - Segmentation fault when OCI8::Cursor#fetch is called prior to
50
+ OCI8::Cursor#exec.
51
+
52
+ - Detailed error message is not reported when PL/SQL NO_DATA_FOUND
53
+ exception is raised.
54
+ (reported by Raimonds Simanovskis)
55
+
1
56
  2.0.3:
2
57
 
3
- * Imcompatible Changes
58
+ * Incompatible Changes
4
59
 
5
60
  - Number column in a SQL statement
6
61
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.3
1
+ 2.0.4
data/dist-files CHANGED
@@ -31,9 +31,12 @@ ext/oci8/oci8.c
31
31
  ext/oci8/oci8.h
32
32
  ext/oci8/oci8lib.c
33
33
  ext/oci8/ocidatetime.c
34
+ ext/oci8/ocihandle.c
34
35
  ext/oci8/ocinumber.c
35
36
  ext/oci8/oraconf.rb
36
37
  ext/oci8/oradate.c
38
+ ext/oci8/oranumber_util.c
39
+ ext/oci8/oranumber_util.h
37
40
  ext/oci8/post-config.rb
38
41
  ext/oci8/stmt.c
39
42
  ext/oci8/object.c
@@ -3,6 +3,7 @@
3
3
  #env.c
4
4
 
5
5
  oci8.c
6
+ ocihandle.c
6
7
  bind.c
7
8
  stmt.c
8
9
  encoding.c
@@ -947,6 +947,23 @@ OCILobOpen_nb:
947
947
  - OCILobLocator *locp
948
948
  - ub1 mode
949
949
 
950
+ OCIMessageOpen:
951
+ :version: 810
952
+ :args: - dvoid *envhp
953
+ - OCIError *errhp
954
+ - OCIMsg **msghp
955
+ - CONST OraText *product
956
+ - CONST OraText *facility
957
+ - OCIDuration dur
958
+
959
+ OCIMessageGet:
960
+ :version: 810
961
+ :ret: OraText *
962
+ :args: - OCIMsg *msgh
963
+ - ub4 msgno
964
+ - OraText *msgbuf
965
+ - size_t buflen
966
+
950
967
  # round trip: 0
951
968
  OCINumberIsInt:
952
969
  :version: 810
@@ -3,7 +3,7 @@
3
3
  * attr.c
4
4
  *
5
5
  * $Author: kubo $
6
- * $Date: 2009-10-21 22:50:01 +0900 (Wed, 21 Oct 2009) $
6
+ * $Date: 2009-05-17 19:08:39 +0900 (Sun, 17 May 2009) $
7
7
  *
8
8
  * Copyright (C) 2002-2007 KUBO Takehiro <kubo@jiubao.org>
9
9
  */
@@ -3,7 +3,7 @@
3
3
  * bind.c
4
4
  *
5
5
  * $Author: kubo $
6
- * $Date: 2009-10-21 22:50:01 +0900 (Wed, 21 Oct 2009) $
6
+ * $Date: 2009-05-19 21:51:28 +0900 (Tue, 19 May 2009) $
7
7
  *
8
8
  * Copyright (C) 2002-2008 KUBO Takehiro <kubo@jiubao.org>
9
9
  */
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  error.c - part of ruby-oci8
4
4
 
5
- Copyright (C) 2002-2007 KUBO Takehiro <kubo@jiubao.org>
5
+ Copyright (C) 2002-2010 KUBO Takehiro <kubo@jiubao.org>
6
6
 
7
7
  =begin
8
8
  == OCIError
@@ -28,9 +28,58 @@ static ID oci8_id_sql;
28
28
  static ID oci8_id_caller;
29
29
  static ID oci8_id_set_backtrace;
30
30
 
31
+ #define ERRBUF_EXPAND_LEN 256
32
+ static char *errbuf;
33
+ static ub4 errbufsiz;
34
+
35
+ static OCIMsg *msghp;
36
+
37
+ #ifndef OCI_DURATION_PROCESS
38
+ #define OCI_DURATION_PROCESS ((OCIDuration)5)
39
+ #endif
40
+
31
41
  NORETURN(static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line));
32
42
  NORETURN(static void set_backtrace_and_raise(VALUE exc, const char *file, int line));
33
43
 
44
+ static VALUE get_error_msg(dvoid *errhp, ub4 type, const char *default_msg, sb4 *errcode_p)
45
+ {
46
+ sword rv;
47
+ size_t len;
48
+
49
+ retry:
50
+ errbuf[0] = '\0';
51
+ rv = OCIErrorGet(errhp, 1, NULL, errcode_p, TO_ORATEXT(errbuf), errbufsiz, type);
52
+ /* OCI manual says:
53
+ * If type is set to OCI_HTYPE_ERROR, then the return
54
+ * code during truncation for OCIErrorGet() is
55
+ * OCI_ERROR. The client can then specify a bigger
56
+ * buffer and call OCIErrorGet() again.
57
+ *
58
+ * But as far as I tested on Oracle XE 10.2.0.1, the return
59
+ * code is OCI_SUCCESS when the message is truncated.
60
+ */
61
+ len = strlen(errbuf);
62
+ if (errbufsiz - len <= 7) {
63
+ /* The error message may be truncated.
64
+ * The magic number 7 means the maximum length of one utf-8
65
+ * character plus the length of a nul terminator.
66
+ */
67
+ errbufsiz += ERRBUF_EXPAND_LEN;
68
+ errbuf = xrealloc(errbuf, errbufsiz);
69
+ goto retry;
70
+ }
71
+ if (rv != OCI_SUCCESS) {
72
+ /* No message is found. Use the default message. */
73
+ return rb_usascii_str_new_cstr(default_msg);
74
+ }
75
+
76
+ /* truncate trailing CR and LF */
77
+ while (len > 0 && (errbuf[len - 1] == '\n' || errbuf[len - 1] == '\r')) {
78
+ len--;
79
+ }
80
+ return rb_external_str_new_with_enc(errbuf, len, oci8_encoding);
81
+ }
82
+
34
83
  static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line)
35
84
  {
36
85
  VALUE vcodes = Qnil;
@@ -103,7 +152,7 @@ static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, c
103
152
  break;
104
153
  case OCI_NO_DATA:
105
154
  exc = eOCINoData;
106
- msg = rb_usascii_str_new_cstr("No Data");
155
+ msg = get_error_msg(errhp, type, "No Data", &errcode);
107
156
  break;
108
157
  case OCI_INVALID_HANDLE:
109
158
  exc = eOCIInvalidHandle;
@@ -252,6 +301,9 @@ sb4 oci8_get_error_code(OCIError *errhp)
252
301
 
253
302
  void Init_oci8_error(void)
254
303
  {
304
+ errbufsiz = ERRBUF_EXPAND_LEN;
305
+ errbuf = xmalloc(errbufsiz);
306
+
255
307
  oci8_id_code = rb_intern("code");
256
308
  oci8_id_message = rb_intern("message");
257
309
  oci8_id_parse_error_offset = rb_intern("parse_error_offset");
@@ -301,3 +353,40 @@ void oci8_do_raise_init_error(const char *file, int line)
301
353
  rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
302
354
  set_backtrace_and_raise(exc, file, line);
303
355
  }
356
+
357
+ VALUE oci8_get_error_message(ub4 msgno, const char *default_msg)
358
+ {
359
+ char head[32];
360
+ size_t headsz;
361
+ const char *errmsg = NULL;
362
+ char msgbuf[64];
363
+
364
+ if (have_OCIMessageGet) {
365
+ if (msghp == NULL) {
366
+ oci_lc(OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_ORATEXT("rdbms"), TO_ORATEXT("ora"), OCI_DURATION_PROCESS));
367
+ }
368
+ errmsg = TO_CHARPTR(OCIMessageGet(msghp, msgno, NULL, 0));
369
+ }
370
+ if (errmsg == NULL) {
371
+ if (default_msg != NULL) {
372
+ errmsg = default_msg;
373
+ } else {
374
+ /* last resort */
375
+ snprintf(msgbuf, sizeof(msgbuf), "Message %u not found; product=rdbms; facility=ora", msgno);
376
+ errmsg = msgbuf;
377
+ }
378
+ }
379
+ headsz = snprintf(head, sizeof(head), "ORA-%05u: ", msgno);
380
+ return rb_str_append(rb_usascii_str_new(head, headsz),
381
+ rb_external_str_new_with_enc(errmsg, strlen(errmsg), oci8_encoding));
382
+ }
383
+
384
+ void oci8_do_raise_by_msgno(ub4 msgno, const char *default_msg, const char *file, int line)
385
+ {
386
+ VALUE msg = oci8_get_error_message(msgno, default_msg);
387
+ VALUE exc = rb_funcall(eOCIError, oci8_id_new, 1, msg);
388
+
389
+ rb_ivar_set(exc, oci8_id_code, rb_ary_new3(1, INT2FIX(-1)));
390
+ rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
391
+ set_backtrace_and_raise(exc, file, line);
392
+ }
@@ -69,6 +69,7 @@ have_type('OCIInterval*', 'ociap.h')
69
69
  have_type('OCICallbackLobRead2', 'ociap.h')
70
70
  have_type('OCICallbackLobWrite2', 'ociap.h')
71
71
  have_type('OCIAdmin*', 'ociap.h')
72
+ have_type('OCIMsg*', 'ociap.h')
72
73
 
73
74
  if with_config('oracle-version')
74
75
  oci_client_version = OCI8::OracleVersion.new(with_config('oracle-version')).to_i
@@ -82,11 +83,11 @@ if with_config('runtime-check')
82
83
  $libs = saved_libs
83
84
  end
84
85
 
85
- $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o",
86
+ $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
86
87
  "stmt.o", "bind.o", "metadata.o", "attr.o",
87
88
  "lob.o", "oradate.o",
88
89
  "ocinumber.o", "ocidatetime.o", "object.o", "apiwrap.o",
89
- "encoding.o", "xmldb.o"]
90
+ "encoding.o", "xmldb.o", "oranumber_util.o"]
90
91
 
91
92
  if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
92
93
  $defs << "-DUSE_WIN32_C"
@@ -300,11 +300,11 @@ static VALUE oci8_named_coll_set_coll_element(VALUE self, VALUE datatype, VALUE
300
300
  switch (FIX2INT(datatype)) {
301
301
  case ATTR_STRING:
302
302
  oci_lc(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_VARCHAR2, NULL, NULL, OCI_DURATION_SESSION, TRUE, &cb_data.data.ptr));
303
- oci_lc(OCIObjectGetInd(oci8_envhp, oci8_errhp, cb_data.data.ptr, (dvoid**)&cb_data.indp));
303
+ cb_data.indp = &cb_data.ind;
304
304
  break;
305
305
  case ATTR_RAW:
306
306
  oci_lc(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_RAW, NULL, NULL, OCI_DURATION_SESSION, TRUE, &cb_data.data.ptr));
307
- oci_lc(OCIObjectGetInd(oci8_envhp, oci8_errhp, cb_data.data.ptr, (dvoid**)&cb_data.indp));
307
+ cb_data.indp = &cb_data.ind;
308
308
  break;
309
309
  case ATTR_OCINUMBER:
310
310
  case ATTR_FLOAT:
@@ -498,11 +498,14 @@ static void bind_named_type_mark(oci8_base_t *base)
498
498
  {
499
499
  oci8_bind_t *obind = (oci8_bind_t *)base;
500
500
  oci8_hp_obj_t *oho = (oci8_hp_obj_t *)obind->valuep;
501
- ub4 idx = 0;
502
501
 
503
- do {
504
- rb_gc_mark(oho[idx].obj);
505
- } while (++idx < obind->maxar_sz);
502
+ if (oho != NULL) {
503
+ ub4 idx = 0;
504
+
505
+ do {
506
+ rb_gc_mark(oho[idx].obj);
507
+ } while (++idx < obind->maxar_sz);
508
+ }
506
509
  rb_gc_mark(obind->tdo);
507
510
  }
508
511
 
@@ -510,14 +513,18 @@ static void bind_named_type_free(oci8_base_t *base)
510
513
  {
511
514
  oci8_bind_t *obind = (oci8_bind_t *)base;
512
515
  oci8_hp_obj_t *oho = (oci8_hp_obj_t *)obind->valuep;
513
- ub4 idx = 0;
514
516
 
515
- do {
516
- if (oho[idx].hp != NULL) {
517
- OCIObjectFree(oci8_envhp, oci8_errhp, oho[idx].hp, OCI_DEFAULT);
518
- oho[idx].hp = NULL;
519
- }
520
- } while (++idx < obind->maxar_sz);
517
+ if (oho != NULL) {
518
+ ub4 idx = 0;
519
+
520
+ do {
521
+ if (oho[idx].hp != NULL) {
522
+ OCIObjectFree(oci8_envhp, oci8_errhp, oho[idx].hp, OCI_DEFAULT);
523
+ oho[idx].hp = NULL;
524
+ }
525
+ } while (++idx < obind->maxar_sz);
526
+ }
527
+ oci8_bind_free(base);
521
528
  }
522
529
 
523
530
  static VALUE bind_named_type_get(oci8_bind_t *obind, void *data, void *null_struct)
@@ -64,6 +64,28 @@ static VALUE oci8_s_oracle_client_vernum(VALUE klass)
64
64
  return oracle_client_vernum;
65
65
  }
66
66
 
67
+ /*
68
+ * call-seq:
69
+ * OCI8.error_message(message_no) -> string
70
+ *
71
+ * Get the Oracle error message specified by message_no.
72
+ * Its language depends on NLS_LANGUAGE.
73
+ *
74
+ * Note: This method is unavailable if the Oracle client version is 8.0.
75
+ *
76
+ * example:
77
+ * # When NLS_LANG is AMERICAN_AMERICA.AL32UTF8
78
+ * OCI8.error_message(1) # => "ORA-00001: unique constraint (%s.%s) violated"
79
+ *
80
+ * # When NLS_LANG is FRENCH_FRANCE.AL32UTF8
81
+ * OCI8.error_message(1) # => "ORA-00001: violation de contrainte unique (%s.%s)"
82
+ *
83
+ */
84
+ static VALUE oci8_s_error_message(VALUE klass, VALUE msgid)
85
+ {
86
+ return oci8_get_error_message(NUM2UINT(msgid), NULL);
87
+ }
88
+
67
89
  #define CONN_STR_REGEX "/^([^(\\s|\\@)]*)\\/([^(\\s|\\@)]*)(?:\\@(\\S+))?(?:\\s+as\\s+(\\S*)\\s*)?$/i"
68
90
  static void oci8_do_parse_connect_string(VALUE conn_str, VALUE *user, VALUE *pass, VALUE *dbname, VALUE *mode)
69
91
  {
@@ -942,6 +964,9 @@ VALUE Init_oci8(void)
942
964
  id_set_prefetch_rows = rb_intern("prefetch_rows=");
943
965
 
944
966
  rb_define_singleton_method_nodoc(cOCI8, "oracle_client_vernum", oci8_s_oracle_client_vernum, 0);
967
+ if (have_OCIMessageOpen && have_OCIMessageGet) {
968
+ rb_define_singleton_method(cOCI8, "error_message", oci8_s_error_message, 1);
969
+ }
945
970
  rb_define_private_method(cOCI8, "parse_connect_string", oci8_parse_connect_string, 1);
946
971
  rb_define_method(cOCI8, "initialize", oci8_svcctx_initialize, -1);
947
972
  rb_define_method(cOCI8, "logoff", oci8_svcctx_logoff, 0);