ruby-oci8 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/ChangeLog +366 -19
  2. data/Makefile +2 -8
  3. data/NEWS +111 -0
  4. data/README +4 -85
  5. data/VERSION +1 -1
  6. data/dist-files +9 -2
  7. data/ext/oci8/.document +1 -0
  8. data/ext/oci8/apiwrap.c.tmpl +12 -2
  9. data/ext/oci8/apiwrap.yml +37 -21
  10. data/ext/oci8/attr.c +23 -74
  11. data/ext/oci8/bind.c +93 -225
  12. data/ext/oci8/connection_pool.c +201 -0
  13. data/ext/oci8/encoding.c +117 -24
  14. data/ext/oci8/env.c +5 -10
  15. data/ext/oci8/error.c +171 -189
  16. data/ext/oci8/extconf.rb +6 -2
  17. data/ext/oci8/lob.c +81 -79
  18. data/ext/oci8/metadata.c +42 -177
  19. data/ext/oci8/object.c +55 -28
  20. data/ext/oci8/oci8.c +426 -294
  21. data/ext/oci8/oci8.h +84 -51
  22. data/ext/oci8/oci8lib.c +75 -53
  23. data/ext/oci8/ocidatetime.c +67 -88
  24. data/ext/oci8/ocihandle.c +78 -37
  25. data/ext/oci8/ocinumber.c +166 -109
  26. data/ext/oci8/oraconf.rb +68 -157
  27. data/ext/oci8/oradate.c +2 -7
  28. data/ext/oci8/stmt.c +40 -183
  29. data/ext/oci8/thread_util.c +85 -0
  30. data/ext/oci8/thread_util.h +30 -0
  31. data/lib/oci8.rb.in +19 -13
  32. data/lib/oci8/.document +2 -0
  33. data/lib/oci8/bindtype.rb +62 -45
  34. data/lib/oci8/connection_pool.rb +118 -0
  35. data/lib/oci8/datetime.rb +304 -320
  36. data/lib/oci8/encoding-init.rb +62 -30
  37. data/lib/oci8/encoding.yml +3 -3
  38. data/lib/oci8/metadata.rb +552 -497
  39. data/lib/oci8/object.rb +9 -9
  40. data/lib/oci8/oci8.rb +161 -2
  41. data/lib/oci8/ocihandle.rb +427 -0
  42. data/lib/oci8/properties.rb +31 -1
  43. data/ruby-oci8.gemspec +10 -3
  44. data/test/README +41 -3
  45. data/test/config.rb +16 -0
  46. data/test/test_all.rb +3 -0
  47. data/test/test_bind_string.rb +106 -0
  48. data/test/test_break.rb +33 -7
  49. data/test/test_clob.rb +13 -10
  50. data/test/test_connection_pool.rb +125 -0
  51. data/test/test_connstr.rb +2 -2
  52. data/test/test_datetime.rb +26 -66
  53. data/test/test_encoding.rb +7 -3
  54. data/test/test_error.rb +88 -0
  55. data/test/test_metadata.rb +1356 -204
  56. data/test/test_oci8.rb +27 -8
  57. data/test/test_oranumber.rb +41 -0
  58. metadata +34 -9
  59. data/ext/oci8/xmldb.c +0 -383
@@ -2,11 +2,8 @@
2
2
  /*
3
3
  error.c - part of ruby-oci8
4
4
 
5
- Copyright (C) 2002-2010 KUBO Takehiro <kubo@jiubao.org>
5
+ Copyright (C) 2002-2011 KUBO Takehiro <kubo@jiubao.org>
6
6
 
7
- =begin
8
- == OCIError
9
- =end
10
7
  */
11
8
  #include "oci8.h"
12
9
 
@@ -25,10 +22,9 @@ static VALUE eOCIStillExecuting;
25
22
  static VALUE eOCIContinue;
26
23
  static VALUE eOCISuccessWithInfo;
27
24
 
28
- static ID oci8_id_code;
29
- static ID oci8_id_message;
30
- static ID oci8_id_parse_error_offset;
31
- static ID oci8_id_sql;
25
+ static ID oci8_id_at_code;
26
+ static ID oci8_id_at_sql;
27
+ static ID oci8_id_at_parse_error_offset;
32
28
  static ID oci8_id_caller;
33
29
  static ID oci8_id_set_backtrace;
34
30
 
@@ -38,12 +34,7 @@ static ub4 errbufsiz;
38
34
 
39
35
  static OCIMsg *msghp;
40
36
 
41
- #ifndef OCI_DURATION_PROCESS
42
- #define OCI_DURATION_PROCESS ((OCIDuration)5)
43
- #endif
44
-
45
- NORETURN(static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line));
46
- NORETURN(static void set_backtrace_and_raise(VALUE exc, const char *file, int line));
37
+ static VALUE set_backtrace(VALUE exc, const char *file, int line);
47
38
 
48
39
  static VALUE get_error_msg(dvoid *errhp, ub4 type, const char *default_msg, sb4 *errcode_p)
49
40
  {
@@ -84,79 +75,32 @@ retry:
84
75
  return rb_external_str_new_with_enc(errbuf, len, oci8_encoding);
85
76
  }
86
77
 
87
- static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line)
78
+ static VALUE oci8_make_exc(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line)
88
79
  {
89
- VALUE vcodes = Qnil;
90
- VALUE vmessages = Qnil;
91
80
  VALUE exc;
92
- char errmsg[1024];
93
- sb4 errcode;
94
- ub4 recodeno;
81
+ char errmsg[128];
82
+ sb4 errcode = -1;
95
83
  VALUE msg;
96
- #ifdef OCI_ATTR_PARSE_ERROR_OFFSET
97
- VALUE vparse_error_offset = Qnil;
98
- #endif
99
- #ifdef OCI_ATTR_STATEMENT
100
- VALUE vsql = Qnil;
101
- #endif
102
- int i;
84
+ VALUE parse_error_offset = Qnil;
85
+ VALUE sql = Qnil;
103
86
  int rv;
87
+ int numarg = 1;
104
88
 
105
89
  switch (status) {
106
90
  case OCI_ERROR:
91
+ exc = eOCIError;
92
+ msg = get_error_msg(errhp, type, "Error", &errcode);
93
+ numarg = 4;
94
+ break;
107
95
  case OCI_SUCCESS_WITH_INFO:
108
- vcodes = rb_ary_new();
109
- vmessages = rb_ary_new();
110
- for (recodeno = 1;;recodeno++) {
111
- /* get error string */
112
- rv = OCIErrorGet(errhp, recodeno, NULL, &errcode, TO_ORATEXT(errmsg), sizeof(errmsg), type);
113
- if (rv != OCI_SUCCESS) {
114
- break;
115
- }
116
- /* chop error string */
117
- for (i = strlen(errmsg) - 1;i >= 0;i--) {
118
- if (errmsg[i] == '\n' || errmsg[i] == '\r') {
119
- errmsg[i] = '\0';
120
- } else {
121
- break;
122
- }
123
- }
124
- rb_ary_push(vcodes, INT2FIX(errcode));
125
- rb_ary_push(vmessages, rb_external_str_new_with_enc(errmsg, strlen(errmsg), oci8_encoding));
126
- }
127
- if (RARRAY_LEN(vmessages) > 0) {
128
- msg = RARRAY_PTR(vmessages)[0];
129
- } else {
130
- msg = rb_usascii_str_new_cstr("ERROR");
131
- }
132
- if (status == OCI_ERROR) {
133
- exc = eOCIError;
134
- } else {
135
- exc = eOCISuccessWithInfo;
136
- }
137
- #ifdef OCI_ATTR_PARSE_ERROR_OFFSET
138
- if (stmthp != NULL) {
139
- ub2 offset;
140
- rv = OCIAttrGet(stmthp, OCI_HTYPE_STMT, &offset, 0, OCI_ATTR_PARSE_ERROR_OFFSET, errhp);
141
- if (rv == OCI_SUCCESS) {
142
- vparse_error_offset = INT2FIX(offset);
143
- }
144
- }
145
- #endif
146
- #ifdef OCI_ATTR_STATEMENT
147
- if (stmthp != NULL) {
148
- text *sql;
149
- ub4 size;
150
- rv = OCIAttrGet(stmthp, OCI_HTYPE_STMT, &sql, &size, OCI_ATTR_STATEMENT, errhp);
151
- if (rv == OCI_SUCCESS) {
152
- vsql = rb_external_str_new_with_enc(TO_CHARPTR(sql), size, oci8_encoding);
153
- }
154
- }
155
- #endif
96
+ exc = eOCISuccessWithInfo;
97
+ msg = get_error_msg(errhp, type, "Error", &errcode);
98
+ numarg = 4;
156
99
  break;
157
100
  case OCI_NO_DATA:
158
101
  exc = eOCINoData;
159
102
  msg = get_error_msg(errhp, type, "No Data", &errcode);
103
+ numarg = 4;
160
104
  break;
161
105
  case OCI_INVALID_HANDLE:
162
106
  exc = eOCIInvalidHandle;
@@ -176,30 +120,28 @@ static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, c
176
120
  break;
177
121
  default:
178
122
  sprintf(errmsg, "Unknown error (%d)", status);
179
- exc = rb_eStandardError;
123
+ exc = eOCIException;
180
124
  msg = rb_usascii_str_new_cstr(errmsg);
181
125
  }
182
- exc = rb_funcall(exc, oci8_id_new, 1, msg);
183
- if (!NIL_P(vcodes)) {
184
- rb_ivar_set(exc, oci8_id_code, vcodes);
185
- }
186
- if (!NIL_P(vmessages)) {
187
- rb_ivar_set(exc, oci8_id_message, vmessages);
188
- }
189
- #ifdef OCI_ATTR_PARSE_ERROR_OFFSET
190
- if (!NIL_P(vparse_error_offset)) {
191
- rb_ivar_set(exc, oci8_id_parse_error_offset, vparse_error_offset);
192
- }
193
- #endif
194
- #ifdef OCI_ATTR_STATEMENT
195
- if (!NIL_P(vsql)) {
196
- rb_ivar_set(exc, oci8_id_sql, vsql);
126
+ if (stmthp != NULL) {
127
+ ub2 offset;
128
+ text *text;
129
+ ub4 size;
130
+
131
+ rv = OCIAttrGet(stmthp, OCI_HTYPE_STMT, &offset, 0, OCI_ATTR_PARSE_ERROR_OFFSET, errhp);
132
+ if (rv == OCI_SUCCESS) {
133
+ parse_error_offset = INT2FIX(offset);
134
+ }
135
+ rv = OCIAttrGet(stmthp, OCI_HTYPE_STMT, &text, &size, OCI_ATTR_STATEMENT, errhp);
136
+ if (rv == OCI_SUCCESS) {
137
+ sql = rb_external_str_new_with_enc(TO_CHARPTR(text), size, oci8_encoding);
138
+ }
197
139
  }
198
- #endif
199
- set_backtrace_and_raise(exc, file, line);
140
+ exc = rb_funcall(exc, oci8_id_new, numarg, msg, INT2FIX(errcode), sql, parse_error_offset);
141
+ return set_backtrace(exc, file, line);
200
142
  }
201
143
 
202
- static void set_backtrace_and_raise(VALUE exc, const char *file, int line)
144
+ static VALUE set_backtrace(VALUE exc, const char *file, int line)
203
145
  {
204
146
  char errmsg[64];
205
147
  VALUE backtrace;
@@ -215,87 +157,30 @@ static void set_backtrace_and_raise(VALUE exc, const char *file, int line)
215
157
  rb_ary_unshift(backtrace, rb_usascii_str_new_cstr(errmsg));
216
158
  rb_funcall(exc, oci8_id_set_backtrace, 1, backtrace);
217
159
  }
218
- rb_exc_raise(exc);
160
+ return exc;
219
161
  }
220
162
 
163
+ /*
164
+ * call-seq:
165
+ * OCI8.new(message, code = nil, sql = nil, parse_error_offset = nil)
166
+ *
167
+ * Creates a new OCIError object.
168
+ */
221
169
  static VALUE oci8_error_initialize(int argc, VALUE *argv, VALUE self)
222
170
  {
223
171
  VALUE msg;
224
172
  VALUE code;
173
+ VALUE sql;
174
+ VALUE parse_error_offset;
225
175
 
226
- rb_scan_args(argc, argv, "02", &msg, &code);
176
+ rb_scan_args(argc, argv, "04", &msg, &code, &sql, &parse_error_offset);
227
177
  rb_call_super(argc > 1 ? 1 : argc, argv);
228
- if (!NIL_P(code)) {
229
- rb_ivar_set(self, oci8_id_code, rb_ary_new3(1, code));
230
- }
178
+ rb_ivar_set(self, oci8_id_at_code, code);
179
+ rb_ivar_set(self, oci8_id_at_sql, sql);
180
+ rb_ivar_set(self, oci8_id_at_parse_error_offset, parse_error_offset);
231
181
  return Qnil;
232
182
  }
233
183
 
234
- /*
235
- =begin
236
- --- OCIError#code()
237
- =end
238
- */
239
- static VALUE oci8_error_code(VALUE self)
240
- {
241
- VALUE ary = rb_ivar_get(self, oci8_id_code);
242
- if (RARRAY_LEN(ary) == 0) {
243
- return Qnil;
244
- }
245
- return RARRAY_PTR(ary)[0];
246
- }
247
-
248
- /*
249
- =begin
250
- --- OCIError#codes()
251
- =end
252
- */
253
- static VALUE oci8_error_code_array(VALUE self)
254
- {
255
- return rb_ivar_get(self, oci8_id_code);
256
- }
257
-
258
- /*
259
- =begin
260
- --- OCIError#message()
261
- =end
262
- */
263
-
264
- /*
265
- =begin
266
- --- OCIError#messages()
267
- =end
268
- */
269
- static VALUE oci8_error_message_array(VALUE self)
270
- {
271
- return rb_ivar_get(self, oci8_id_message);
272
- }
273
-
274
- #ifdef OCI_ATTR_PARSE_ERROR_OFFSET
275
- /*
276
- =begin
277
- --- OCIError#parseErrorOffset()
278
- =end
279
- */
280
- static VALUE oci8_error_parse_error_offset(VALUE self)
281
- {
282
- return rb_ivar_get(self, oci8_id_parse_error_offset);
283
- }
284
- #endif
285
-
286
- #ifdef OCI_ATTR_STATEMENT
287
- /*
288
- =begin
289
- --- OCIError#sql()
290
- (Oracle 8.1.6 or later)
291
- =end
292
- */
293
- static VALUE oci8_error_sql(VALUE self)
294
- {
295
- return rb_ivar_get(self, oci8_id_sql);
296
- }
297
- #endif
298
-
299
184
  sb4 oci8_get_error_code(OCIError *errhp)
300
185
  {
301
186
  sb4 errcode = -1;
@@ -308,18 +193,17 @@ void Init_oci8_error(void)
308
193
  errbufsiz = ERRBUF_EXPAND_LEN;
309
194
  errbuf = xmalloc(errbufsiz);
310
195
 
311
- oci8_id_code = rb_intern("code");
312
- oci8_id_message = rb_intern("message");
313
- oci8_id_parse_error_offset = rb_intern("parse_error_offset");
314
- oci8_id_sql = rb_intern("sql");
196
+ oci8_id_at_code = rb_intern("@code");
197
+ oci8_id_at_sql = rb_intern("@sql");
198
+ oci8_id_at_parse_error_offset = rb_intern("@parse_error_offset");
315
199
  oci8_id_caller = rb_intern("caller");
316
200
  oci8_id_set_backtrace = rb_intern("set_backtrace");
317
201
 
318
202
  eOCIException = rb_define_class("OCIException", rb_eStandardError);
319
203
  eOCIBreak = rb_define_class("OCIBreak", eOCIException);
320
204
 
321
- eOCINoData = rb_define_class("OCINoData", eOCIException);
322
205
  eOCIError = rb_define_class("OCIError", eOCIException);
206
+ eOCINoData = rb_define_class("OCINoData", eOCIError);
323
207
  eOCIInvalidHandle = rb_define_class("OCIInvalidHandle", eOCIException);
324
208
  eOCINeedData = rb_define_class("OCINeedData", eOCIException);
325
209
  eOCIStillExecuting = rb_define_class("OCIStillExecuting", eOCIException);
@@ -327,35 +211,28 @@ void Init_oci8_error(void)
327
211
  eOCISuccessWithInfo = rb_define_class("OCISuccessWithInfo", eOCIError);
328
212
 
329
213
  rb_define_method(eOCIError, "initialize", oci8_error_initialize, -1);
330
- rb_define_method(eOCIError, "code", oci8_error_code, 0);
331
- rb_define_method(eOCIError, "codes", oci8_error_code_array, 0);
332
- rb_define_method(eOCIError, "messages", oci8_error_message_array, 0);
333
- #ifdef OCI_ATTR_PARSE_ERROR_OFFSET
334
- rb_define_method(eOCIError, "parseErrorOffset", oci8_error_parse_error_offset, 0);
335
- #endif
336
- #ifdef OCI_ATTR_STATEMENT
337
- rb_define_method(eOCIError, "sql", oci8_error_sql, 0);
338
- #endif
214
+ rb_define_attr(eOCIError, "code", 1, 0);
215
+ rb_define_attr(eOCIError, "sql", 1, 0);
216
+ rb_define_attr(eOCIError, "parse_error_offset", 1, 0);
217
+ rb_define_alias(eOCIError, "parseErrorOffset", "parse_error_offset");
339
218
  }
340
219
 
341
220
  void oci8_do_raise(OCIError *errhp, sword status, OCIStmt *stmthp, const char *file, int line)
342
221
  {
343
- oci8_raise2(errhp, status, OCI_HTYPE_ERROR, stmthp, file, line);
222
+ rb_exc_raise(oci8_make_exc(errhp, status, OCI_HTYPE_ERROR, stmthp, file, line));
344
223
  }
345
224
 
346
225
  void oci8_do_env_raise(OCIEnv *envhp, sword status, const char *file, int line)
347
226
  {
348
- oci8_raise2(envhp, status, OCI_HTYPE_ENV, NULL, file, line);
227
+ rb_exc_raise(oci8_make_exc(envhp, status, OCI_HTYPE_ENV, NULL, file, line));
349
228
  }
350
229
 
351
230
  void oci8_do_raise_init_error(const char *file, int line)
352
231
  {
353
232
  VALUE msg = rb_usascii_str_new_cstr("OCI Library Initialization Error");
354
- VALUE exc = rb_funcall(eOCIError, oci8_id_new, 1, msg);
233
+ VALUE exc = rb_funcall(eOCIError, oci8_id_new, 2, msg, INT2FIX(-1));
355
234
 
356
- rb_ivar_set(exc, oci8_id_code, rb_ary_new3(1, INT2FIX(-1)));
357
- rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
358
- set_backtrace_and_raise(exc, file, line);
235
+ rb_exc_raise(set_backtrace(exc, file, line));
359
236
  }
360
237
 
361
238
  VALUE oci8_get_error_message(ub4 msgno, const char *default_msg)
@@ -367,7 +244,7 @@ VALUE oci8_get_error_message(ub4 msgno, const char *default_msg)
367
244
 
368
245
  if (have_OCIMessageGet) {
369
246
  if (msghp == NULL) {
370
- oci_lc(OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_ORATEXT("rdbms"), TO_ORATEXT("ora"), OCI_DURATION_PROCESS));
247
+ chkerr(OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_CONST_ORATEXT("rdbms"), TO_CONST_ORATEXT("ora"), OCI_DURATION_PROCESS));
371
248
  }
372
249
  errmsg = TO_CHARPTR(OCIMessageGet(msghp, msgno, NULL, 0));
373
250
  }
@@ -388,9 +265,114 @@ VALUE oci8_get_error_message(ub4 msgno, const char *default_msg)
388
265
  void oci8_do_raise_by_msgno(ub4 msgno, const char *default_msg, const char *file, int line)
389
266
  {
390
267
  VALUE msg = oci8_get_error_message(msgno, default_msg);
391
- VALUE exc = rb_funcall(eOCIError, oci8_id_new, 1, msg);
268
+ VALUE exc = rb_funcall(eOCIError, oci8_id_new, 2, msg, INT2FIX(-1));
269
+
270
+ rb_exc_raise(set_backtrace(exc, file, line));
271
+ }
392
272
 
393
- rb_ivar_set(exc, oci8_id_code, rb_ary_new3(1, INT2FIX(-1)));
394
- rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
395
- set_backtrace_and_raise(exc, file, line);
273
+ void oci8_check_error_(sword status, oci8_base_t *base, OCIStmt *stmthp, const char *file, int line)
274
+ {
275
+ if (status != OCI_SUCCESS) {
276
+ VALUE exc = oci8_make_exc(oci8_errhp, status, OCI_HTYPE_ERROR, stmthp, file, line);
277
+ while (base != NULL) {
278
+ if (base->type == OCI_HTYPE_SVCCTX) {
279
+ rb_ivar_set(base->self, oci8_id_at_last_error, exc);
280
+ break;
281
+ }
282
+ base = base->parent;
283
+ }
284
+ if (status != OCI_SUCCESS_WITH_INFO) {
285
+ rb_exc_raise(exc);
286
+ }
287
+ }
396
288
  }
289
+
290
+ /*
291
+ * Document-class: OCIException
292
+ *
293
+ * The superclass for all exceptions raised by ruby-oci8.
294
+ *
295
+ * The following exceptions are defined as subclasses of OCIException
296
+ * These exceptions are raised when Oracle Call Interface functions
297
+ * return with an error status.
298
+ *
299
+ * - OCIBreak
300
+ * - OCIContinue
301
+ * - OCIError
302
+ * - OCISuccessWithInfo
303
+ * - OCINoData (It had been a subclass of OCIException, not OCIError, until ruby-oci8 2.0)
304
+ * - OCIInvalidHandle
305
+ * - OCINeedData
306
+ * - OCIStillExecuting
307
+ */
308
+
309
+ /*
310
+ * Document-class: OCIBreak
311
+ *
312
+ * Subclass of OCIException
313
+ *
314
+ * Raised when a SQL execution is cancelled by OCI8#break.
315
+ */
316
+
317
+ /*
318
+ * Document-class: OCINoData
319
+ *
320
+ * Subclass of OCIError from ruby-oci8 2.1.
321
+ * It had been a subclass of OCIException until ruby-oci8 2.0.
322
+ *
323
+ * Raised when PL/SQL NO_DATA_FOUND exception is got.
324
+ */
325
+
326
+ /*
327
+ * Document-class: OCIError
328
+ *
329
+ * Subclass of OCIException
330
+ *
331
+ * The following exceptions are defined as subclasses of OCIError.
332
+ *
333
+ * - OCISuccessWithInfo
334
+ * - OCINoData (It had been a subclass of OCIException, not OCIError, until ruby-oci8 2.0)
335
+ *
336
+ * Raised when underlying Oracle Call Interface failed with an Oracle error code
337
+ * such as ORA-00001.
338
+ */
339
+
340
+ /*
341
+ * Document-class: OCIInvalidHandle
342
+ *
343
+ * Subclass of OCIException
344
+ *
345
+ * Raised when an invalid handle is passed to underlying Oracle Call Interface.
346
+ * Report to the ruby-oci8 author if it is raised.
347
+ */
348
+
349
+ /*
350
+ * Document-class: OCINeedData
351
+ *
352
+ * Subclass of OCIException
353
+ *
354
+ * Report to the ruby-oci8 author if it is raised.
355
+ */
356
+
357
+ /*
358
+ * Document-class: OCIStillExecuting
359
+ *
360
+ * Subclass of OCIError
361
+ *
362
+ * Report to the ruby-oci8 author if it is raised.
363
+ */
364
+
365
+ /*
366
+ * Document-class: OCIContinue
367
+ *
368
+ * Subclass of OCIException
369
+ *
370
+ * Report to the ruby-oci8 author if it is raised.
371
+ */
372
+
373
+ /*
374
+ * Document-class: OCISuccessWithInfo
375
+ *
376
+ * Subclass of OCIError
377
+ *
378
+ */