ruby-oci8 2.2.5.1 → 2.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,71 @@
1
+ 2018-08-22 Kubo Takehiro <kubo@jiubao.org>
2
+ * NEWS: Add changes between 2.2.5.1 and 2.2.6
3
+ * lib/oci8/version.rb: Update to 2.2.6
4
+ * mkpkg-win32.rb: Don't test with self-build ruby 2.4.
5
+
6
+ 2018-08-22 Kubo Takehiro <kubo@jiubao.org>
7
+ * ext/oci8/oci8lib.c: Load a Oracle client library which doesn't have
8
+ version number suffix also on Unix when runtime-check is enabled.
9
+ Oracle version numbers are incremented yearly sice Oracle 18c.
10
+
11
+ 2018-08-22 Kubo Takehiro <kubo@jiubao.org>
12
+ * ext/oci8/hook_funcs.c, ext/oci8/win32.c: Suppress warnings:
13
+ "'raise_error' defined but not used" and "'strncpy' specified
14
+ bound 512 equals destination size".
15
+
16
+ 2018-08-19 Kubo Takehiro <kubo@jiubao.org>
17
+ * ext/oci8/hook_funcs.c: Not depend on Oracle version number
18
+ of Oracle client file name suffix when hooking functions.
19
+ (rsim/oracle-enhanced#1754)
20
+ * test/test_all.rb, test/test_properties.rb, dist-files:
21
+ Add tests for hooking.
22
+
23
+ 2018-08-18 Kubo Takehiro <kubo@jiubao.org>
24
+ * test/test_oci8.rb: Fix to pass a test when the client is 11g
25
+ and the server is 18c. client_driver in v$session_connect_info
26
+ has an extra space at the end.
27
+
28
+ 2018-08-18 Kubo Takehiro <kubo@jiubao.org>
29
+ * ext/oci8/apiwrap.rb, ext/oci8/apiwrap.yml, ext/oci8/oci8.c,
30
+ ext/oci8/oci8.h, lib/oci8/oci8.rb, lib/oci8/oracle_version.rb,
31
+ test/test_oci8.rb: Fix OCI8#oracle_server_version to get
32
+ full version of Oracle 18c.
33
+
34
+ 2018-08-18 Kubo Takehiro <kubo@jiubao.org>
35
+ * lib/oci8/metadata.rb: Update doc comments of OCI8::Metadata::ArgBase#level
36
+ and OCI8::Metadata::ArgBase#arguments.
37
+ * test/test_package_type.rb, lib/oci8.rb: Fix to pass tests on Oracle 18c.
38
+ Detailed user-defined type information used in arguments isn't available
39
+ on Oracle 18c.
40
+
41
+ 2018-08-12 Kubo Takehiro <kubo@jiubao.org>
42
+ * test/test_clob.rb: Skip TestCLob#test_github_issue_20 because it
43
+ takes 4 minutes to test it in my Linux box.
44
+
45
+ 2018-08-12 Kubo Takehiro <kubo@jiubao.org>
46
+ * ext/oci8/lob.c, test/test_clob.rb: LOB#sync, LOB#sync= and LOB#flush
47
+ do nothing now. They have not worked by mistake from the beginning
48
+ because incorrect arguments has been passed to OCILobOpen().
49
+ Moreover it crashed Oracle 18c server-side processes and caused
50
+ "ORA-03113: end-of-file on communication channel."
51
+ (github issue #198)
52
+
53
+ 2018-01-28 Kubo Takehiro <kubo@jiubao.org>
54
+ * ext/oci8/object.c, lib/oci8/object.rb: Add timestamp with time zone
55
+ data type support in object type attributes.
56
+ (github issue #185)
57
+ * test/setup_test_object.sql, test/test_object.rb: Add tests for
58
+ timestamp and timestamp with time zone.
59
+
60
+ 2018-01-23 Kubo Takehiro <kubo@jiubao.org>
61
+ * ext/oci8/object.c, ext/oci8/oci8.h, lib/oci8/object.rb:
62
+ Add timestamp data type support in object type attributes.
63
+ (github issue #185)
64
+
65
+ 2017-12-27 Kubo Takehiro <kubo@jiubao.org>
66
+ * mkpkg-win32.rb: Remove '-rubygems' in the command line to run tests.
67
+ `ubygems.rb` was removed in ruby 2.5.
68
+
1
69
  2017-12-27 Kubo Takehiro <kubo@jiubao.org>
2
70
  * NEWS: Add changes between 2.2.5 and 2.2.5.1
3
71
  * lib/oci8/version.rb: Update to 2.2.5.1
data/NEWS CHANGED
@@ -1,5 +1,44 @@
1
1
  # @markup markdown
2
2
 
3
+ 2.2.6
4
+ =====
5
+
6
+ This release fixed issues about Oracle 18c except one.
7
+
8
+ Fixed issue
9
+ -----------
10
+
11
+ ### Setting some properties failed with Oracle 18c client
12
+
13
+ Setting `OCI8::properties[:tcp_keepalive_time]` or `OCI8::properties[:cancel_read_at_exit]`
14
+ failed with the error message "No shared library is found to hook" when Oracle
15
+ client version is 18c.
16
+ (rsim/oracle-enhanced#1754)
17
+
18
+ ### Fix `OCI8#oracle_server_version` to get full version of Oracle 18c
19
+
20
+ `OCI8#oracle_server_version` returned Oracle version number whose
21
+ number components after the first dot are zeros such as '18.0.0.0.0'
22
+ even when the server version is `18.3.0.0.0`. This issue was fixed by
23
+ using a new OCI function. However the function is available since
24
+ Oracle 18c client. So when the Oracle client version is 12c or earlier
25
+ and the Oracle server version is 18c or later, it cannot get the *full*
26
+ Oracle version number.
27
+
28
+ ### Fix tests when the Oracle server version is 18c.
29
+
30
+ ### LOB#sync, LOB#sync= and LOB#flush do nothing now.
31
+
32
+ They have not worked by mistake from the beginning because incorrect
33
+ arguments has been passed to OCILobOpen(). Moreover it crashed Oracle
34
+ 18c server-side processes and caused "ORA-03113: end-of-file on
35
+ communication channel."
36
+ (github issue #198)
37
+
38
+ ### `unsupported typecode timestamp` when timestamp with time zone is in object type attributes.
39
+
40
+ (github issue #185)
41
+
3
42
  2.2.5.1
4
43
  =======
5
44
 
data/README.md CHANGED
@@ -2,7 +2,7 @@ Ruby-oci8
2
2
  =========
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/ruby-oci8.svg)](http://badge.fury.io/rb/ruby-oci8)
5
- [![Circle CI](https://circleci.com/gh/kubo/ruby-oci8.png?style=shield)](https://circleci.com/gh/kubo/ruby-oci8)
5
+ [![Build Status](https://travis-ci.org/kubo/ruby-oci8.svg?branch=master)](https://travis-ci.org/kubo/ruby-oci8)
6
6
 
7
7
  What is ruby-oci8
8
8
  -----------------
data/dist-files CHANGED
@@ -109,4 +109,5 @@ test/test_oracle_version.rb
109
109
  test/test_oradate.rb
110
110
  test/test_oranumber.rb
111
111
  test/test_package_type.rb
112
+ test/test_properties.rb
112
113
  test/test_rowid.rb
@@ -39,7 +39,7 @@ Download the following packages from [Oracle Technology Network][]
39
39
 
40
40
  * Instant Client Package - Basic (`instantclient-basic-macos.x64-12.1.0.2.0.zip`) or Basic Lite (`instantclient-basiclite-macos.x64-12.1.0.2.0.zip`)
41
41
  * Instant Client Package - SDK (`instantclient-sdk-macos.x64-12.1.0.2.0.zip`)
42
- * Instant Client Package - SQL*Plus (`instantclient-sdk-macos.x64-12.1.0.2.0.zip`) (optionally)
42
+ * Instant Client Package - SQL*Plus (`instantclient-sqlplus-macos.x64-12.1.0.2.0.zip`) (optionally)
43
43
 
44
44
  ### Install Oracle Instant Client Packages via Homebrew
45
45
 
data/ext/oci8/apiwrap.rb CHANGED
@@ -35,7 +35,11 @@ class FuncDef
35
35
  ver_major = (ver / 100)
36
36
  ver_minor = (ver / 10) % 10
37
37
  ver_update = ver % 10
38
- @version = ((ver_major << 24) | (ver_minor << 20) | (ver_update << 12))
38
+ @version = if ver_major >= 18
39
+ ((ver_major << 24) | (ver_minor << 16) | (ver_update << 12))
40
+ else
41
+ ((ver_major << 24) | (ver_minor << 20) | (ver_update << 12))
42
+ end
39
43
  case @version
40
44
  when 0x08000000; @version_num = 'ORAVER_8_0'
41
45
  when 0x08100000; @version_num = 'ORAVER_8_1'
@@ -44,6 +48,7 @@ class FuncDef
44
48
  when 0x0a100000; @version_num = 'ORAVER_10_1'
45
49
  when 0x0a200000; @version_num = 'ORAVER_10_2'
46
50
  when 0x0b100000; @version_num = 'ORAVER_11_1'
51
+ when 0x12000000; @version_num = 'ORAVER_18'
47
52
  end
48
53
  @version_str = "#{ver_major}.#{ver_minor}.#{ver_update}"
49
54
  @ret = val[:ret] || 'sword'
data/ext/oci8/apiwrap.yml CHANGED
@@ -1286,3 +1286,17 @@ OCIPing_nb:
1286
1286
  - OCISvcCtx *svchp
1287
1287
  - OCIError *errhp
1288
1288
  - ub4 mode
1289
+
1290
+ #
1291
+ # Oracle 18.1
1292
+ #
1293
+ OCIServerRelease2:
1294
+ :version: 1800
1295
+ :args:
1296
+ - dvoid *hndlp
1297
+ - OCIError *errhp
1298
+ - OraText *bufp
1299
+ - ub4 bufsz
1300
+ - ub1 hndltype
1301
+ - ub4 *version
1302
+ - ub4 mode
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * hook.c
4
4
  *
5
- * Copyright (C) 2015 Kubo Takehiro <kubo@jiubao.org>
5
+ * Copyright (C) 2015-2018 Kubo Takehiro <kubo@jiubao.org>
6
6
  */
7
7
  #if defined(_WIN32) || defined(__CYGWIN__)
8
8
  #define WINDOWS
@@ -12,15 +12,19 @@
12
12
  #include "plthook.h"
13
13
  #ifdef __CYGWIN__
14
14
  #undef boolean /* boolean defined in oratypes.h coflicts with that in windows.h */
15
+ #define stricmp strcasecmp
16
+ #define strnicmp strncasecmp
15
17
  #endif
16
18
  #ifdef WINDOWS
17
19
  #include <windows.h>
18
20
  #include <mstcpip.h>
21
+ #include <tlhelp32.h>
19
22
  #else
20
23
  #include <unistd.h>
21
24
  #include <sys/socket.h>
22
25
  #include <netinet/in.h>
23
26
  #include <netinet/tcp.h>
27
+ #include <dlfcn.h>
24
28
  #endif
25
29
 
26
30
  #ifdef WINDOWS
@@ -59,6 +63,7 @@ static int WSAAPI hook_setsockopt(SOCKET sockfd, int level, int optname, const v
59
63
  int oci8_tcp_keepalive_time = -1;
60
64
  #endif
61
65
 
66
+ static char hook_errmsg[512];
62
67
 
63
68
  typedef struct {
64
69
  const char *func_name;
@@ -95,35 +100,35 @@ static void socket_entry_clear(socket_entry_t *entry)
95
100
  UNLOCK(&lock);
96
101
  }
97
102
 
98
- static int replace_functions(const char * const *files, hook_func_entry_t *functions)
103
+ static int replace_functions(void *addr, const char *file, hook_func_entry_t *functions)
99
104
  {
105
+ plthook_t *ph;
100
106
  int i;
107
+ int rv = 0;
101
108
 
102
- for (i = 0; files[i] != NULL; i++) {
103
- const char *file = files[i];
104
- plthook_t *ph;
105
- if (plthook_open(&ph, file) == 0) {
106
- int j;
107
- int rv = 0;
108
-
109
- /* install hooks */
110
- for (j = 0; functions[j].func_name != NULL ; j++) {
111
- hook_func_entry_t *function = &functions[j];
112
- rv = plthook_replace(ph, function->func_name, function->func_addr, &function->old_func_addr);
113
- if (rv != 0) {
114
- while (--j >= 0) {
115
- /*restore hooked fuction address */
116
- plthook_replace(ph, functions[j].func_name, functions[j].old_func_addr, NULL);
117
- }
118
- plthook_close(ph);
119
- rb_raise(rb_eRuntimeError, "Could not replace function %s in %s", function->func_name, file);
120
- }
109
+ if (plthook_open_by_address(&ph, addr) != 0) {
110
+ strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
111
+ hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
112
+ return -1;
113
+ }
114
+
115
+ /* install hooks */
116
+ for (i = 0; functions[i].func_name != NULL ; i++) {
117
+ hook_func_entry_t *function = &functions[i];
118
+ rv = plthook_replace(ph, function->func_name, function->func_addr, &function->old_func_addr);
119
+ if (rv != 0) {
120
+ strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
121
+ hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
122
+ while (--i >= 0) {
123
+ /*restore hooked fuction address */
124
+ plthook_replace(ph, functions[i].func_name, functions[i].old_func_addr, NULL);
121
125
  }
122
- plthook_close(ph);
123
- return 0;
126
+ snprintf(hook_errmsg, sizeof(hook_errmsg), "Could not replace function %s in %s", function->func_name, file);
127
+ break;
124
128
  }
125
129
  }
126
- return -1;
130
+ plthook_close(ph);
131
+ return rv;
127
132
  }
128
133
 
129
134
  #ifdef WINDOWS
@@ -142,23 +147,6 @@ static int locK_is_initialized;
142
147
 
143
148
  static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
144
149
 
145
- static const char * const tcp_func_files[] = {
146
- /* full client */
147
- "orantcp12.dll",
148
- "orantcp11.dll",
149
- "orantcp10.dll",
150
- "orantcp9.dll",
151
- /* instant client basic */
152
- "oraociei12.dll",
153
- "oraociei11.dll",
154
- "oraociei10.dll",
155
- /* instant client basic lite */
156
- "oraociicus12.dll",
157
- "oraociicus11.dll",
158
- "oraociicus10.dll",
159
- NULL,
160
- };
161
-
162
150
  static hook_func_entry_t tcp_functions[] = {
163
151
  {"WSARecv", (void*)hook_WSARecv, NULL},
164
152
  {"setsockopt", (void*)hook_setsockopt, NULL},
@@ -190,6 +178,9 @@ void oci8_install_hook_functions()
190
178
  DWORD data;
191
179
  DWORD cbData = sizeof(data);
192
180
  const char *reg_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
181
+ HANDLE hSnapshot;
182
+ MODULEENTRY32 me;
183
+ BOOL module_found = FALSE;
193
184
 
194
185
  if (hook_functions_installed) {
195
186
  return;
@@ -222,7 +213,32 @@ void oci8_install_hook_functions()
222
213
  }
223
214
  RegCloseKey(hKey);
224
215
 
225
- if (replace_functions(tcp_func_files, tcp_functions) != 0) {
216
+ hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
217
+ me.dwSize = sizeof(me);
218
+ if (Module32First(hSnapshot, &me)) {
219
+ do {
220
+ const char *p = NULL;
221
+ if (strnicmp(me.szModule, "orantcp", 7) == 0) { // ORACLE_HOME-based client
222
+ p = me.szModule + 7;
223
+ } else if (strnicmp(me.szModule, "oraociei", 8) == 0) { // instant client basic
224
+ p = me.szModule + 8;
225
+ } else if (strnicmp(me.szModule, "oraociicus", 10) == 0) { // instant client basic lite
226
+ p = me.szModule + 10;
227
+ }
228
+ if (p != NULL && ('1' <= *p && *p <= '9') && ('0' <= *(p + 1) && *(p + 1) <= '9')
229
+ && stricmp(p + 2, ".dll") == 0) {
230
+ if (GetProcAddress((HMODULE)me.modBaseAddr, "nttini") != NULL) {
231
+ module_found = TRUE;
232
+ if (replace_functions(me.modBaseAddr, me.szExePath, tcp_functions) != 0) {
233
+ CloseHandle(hSnapshot);
234
+ rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
235
+ }
236
+ }
237
+ }
238
+ } while (Module32Next(hSnapshot, &me));
239
+ }
240
+ CloseHandle(hSnapshot);
241
+ if (!module_found) {
226
242
  rb_raise(rb_eRuntimeError, "No DLL is found to hook.");
227
243
  }
228
244
  hook_functions_installed = 1;
@@ -241,20 +257,6 @@ static void shutdown_socket(socket_entry_t *entry)
241
257
  #else
242
258
  static ssize_t hook_read(int fd, void *buf, size_t count);
243
259
 
244
- #ifdef __APPLE__
245
- #define SO_EXT "dylib"
246
- #else
247
- #define SO_EXT "so"
248
- #endif
249
-
250
- static const char * const files[] = {
251
- "libclntsh." SO_EXT ".12.1",
252
- "libclntsh." SO_EXT ".11.1",
253
- "libclntsh." SO_EXT ".10.1",
254
- "libclntsh." SO_EXT ".9.0",
255
- NULL,
256
- };
257
-
258
260
  static hook_func_entry_t functions[] = {
259
261
  {"read", (void*)hook_read, NULL},
260
262
  #ifdef SUPPORT_TCP_KEEPALIVE_TIME
@@ -279,16 +281,44 @@ static ssize_t hook_read(int fd, void *buf, size_t count)
279
281
  return rv;
280
282
  }
281
283
 
284
+ static void *ocifunc_addr(void *dlsym_handle, const char **file)
285
+ {
286
+ void *addr = dlsym(dlsym_handle, "OCIEnvCreate");
287
+ Dl_info dli;
288
+
289
+ if (addr == NULL) {
290
+ return NULL;
291
+ }
292
+ if (dladdr(addr, &dli) == 0) {
293
+ return NULL;
294
+ }
295
+ if (strstr(dli.dli_fname, "/libclntsh.so") == 0) {
296
+ return NULL;
297
+ }
298
+ *file = dli.dli_fname;
299
+ return addr;
300
+ }
301
+
282
302
  void oci8_install_hook_functions(void)
283
303
  {
284
304
  static int hook_functions_installed = 0;
305
+ void *addr;
306
+ const char *file;
285
307
 
286
308
  if (hook_functions_installed) {
287
309
  return;
288
310
  }
289
- if (replace_functions(files, functions) != 0) {
311
+ addr = ocifunc_addr(RTLD_DEFAULT, &file);
312
+ if (addr == NULL) {
313
+ /* OCI symbols may be hooked by LD_PRELOAD. */
314
+ addr = ocifunc_addr(RTLD_NEXT, &file);
315
+ }
316
+ if (addr == NULL) {
290
317
  rb_raise(rb_eRuntimeError, "No shared library is found to hook.");
291
318
  }
319
+ if (replace_functions(addr, file, functions) != 0) {
320
+ rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
321
+ }
292
322
  hook_functions_installed = 1;
293
323
  }
294
324
 
data/ext/oci8/lob.c CHANGED
@@ -26,8 +26,6 @@ static VALUE seek_end;
26
26
 
27
27
  enum state {
28
28
  S_NO_OPEN_CLOSE,
29
- S_OPEN,
30
- S_CLOSE,
31
29
  S_BFILE_CLOSE,
32
30
  S_BFILE_OPEN,
33
31
  };
@@ -266,28 +264,6 @@ static ub8 oci8_lob_get_length(oci8_lob_t *lob)
266
264
  return len;
267
265
  }
268
266
 
269
- static void lob_open(oci8_lob_t *lob)
270
- {
271
- if (lob->state == S_CLOSE) {
272
- oci8_svcctx_t *svcctx = check_svcctx(lob);
273
-
274
- chker2(OCILobOpen_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, OCI_DEFAULT),
275
- &svcctx->base);
276
- lob->state = S_OPEN;
277
- }
278
- }
279
-
280
- static void lob_close(oci8_lob_t *lob)
281
- {
282
- if (lob->state == S_OPEN) {
283
- oci8_svcctx_t *svcctx = check_svcctx(lob);
284
-
285
- chker2(OCILobClose_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob),
286
- &svcctx->base);
287
- lob->state = S_CLOSE;
288
- }
289
- }
290
-
291
267
  static void bfile_close(oci8_lob_t *lob)
292
268
  {
293
269
  if (lob->state == S_BFILE_OPEN) {
@@ -368,7 +344,6 @@ static void bfile_close(oci8_lob_t *lob)
368
344
  static VALUE oci8_lob_close(VALUE self)
369
345
  {
370
346
  oci8_lob_t *lob = TO_LOB(self);
371
- lob_close(lob);
372
347
  oci8_base_free(&lob->base);
373
348
  return self;
374
349
  }
@@ -591,7 +566,6 @@ static VALUE oci8_lob_truncate(VALUE self, VALUE len)
591
566
  oci8_lob_t *lob = TO_LOB(self);
592
567
  oci8_svcctx_t *svcctx = check_svcctx(lob);
593
568
 
594
- lob_open(lob);
595
569
  chker2(OCILobTrim2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, NUM2ULL(len)),
596
570
  &svcctx->base);
597
571
  return self;
@@ -740,7 +714,6 @@ static VALUE oci8_lob_read(int argc, VALUE *argv, VALUE self)
740
714
  } while (rv == OCI_NEED_DATA);
741
715
 
742
716
  if (pos >= lob_length) {
743
- lob_close(lob);
744
717
  bfile_close(lob);
745
718
  }
746
719
  lob->pos = pos;
@@ -781,7 +754,6 @@ static VALUE oci8_lob_write(VALUE self, VALUE data)
781
754
  ub8 byte_amt;
782
755
  ub8 char_amt;
783
756
 
784
- lob_open(lob);
785
757
  if (TYPE(data) != T_STRING) {
786
758
  str = rb_obj_as_string(data);
787
759
  } else {
@@ -809,40 +781,32 @@ static VALUE oci8_lob_write(VALUE self, VALUE data)
809
781
  }
810
782
 
811
783
  /*
812
- * @deprecated I'm not sure that this is what the name indicates.
784
+ * @deprecated LOB#sync had not worked by mistake. Do nothing now.
813
785
  * @private
814
786
  */
815
787
  static VALUE oci8_lob_get_sync(VALUE self)
816
788
  {
817
- oci8_lob_t *lob = TO_LOB(self);
818
- return (lob->state == S_NO_OPEN_CLOSE) ? Qtrue : Qfalse;
789
+ rb_warning("LOB#sync had not worked by mistake. Do nothing now.");
790
+ return Qfalse;
819
791
  }
820
792
 
821
793
  /*
822
- * @deprecated I'm not sure that this is what the name indicates.
794
+ * @deprecated LOB#sync had not worked by mistake. Do nothing now.
823
795
  * @private
824
796
  */
825
797
  static VALUE oci8_lob_set_sync(VALUE self, VALUE b)
826
798
  {
827
- oci8_lob_t *lob = TO_LOB(self);
828
- if (RTEST(b)) {
829
- lob_close(lob);
830
- lob->state = S_NO_OPEN_CLOSE;
831
- } else {
832
- if (lob->state == S_NO_OPEN_CLOSE)
833
- lob->state = S_CLOSE;
834
- }
799
+ rb_warning("LOB#sync had not worked by mistake. Do nothing now.");
835
800
  return b;
836
801
  }
837
802
 
838
803
  /*
839
- * @deprecated I'm not sure that this is what the name indicates.
804
+ * @deprecated LOB#flush had not worked by mistake. Do nothing now.
840
805
  * @private
841
806
  */
842
807
  static VALUE oci8_lob_flush(VALUE self)
843
808
  {
844
- oci8_lob_t *lob = TO_LOB(self);
845
- lob_close(lob);
809
+ rb_warning("LOB#flush had not worked by mistake. Do nothing now.");
846
810
  return self;
847
811
  }
848
812
 
data/ext/oci8/object.c CHANGED
@@ -44,6 +44,8 @@ enum {
44
44
  ATTR_FLOAT,
45
45
  ATTR_INTEGER,
46
46
  ATTR_OCIDATE,
47
+ ATTR_TIMESTAMP,
48
+ ATTR_TIMESTAMP_TZ,
47
49
  ATTR_BINARY_DOUBLE,
48
50
  ATTR_BINARY_FLOAT,
49
51
  ATTR_NAMED_TYPE,
@@ -235,6 +237,10 @@ static VALUE get_attribute(VALUE self, VALUE datatype, VALUE typeinfo, void *dat
235
237
  return oci8_make_integer((OCINumber *)data, oci8_errhp);
236
238
  case ATTR_OCIDATE:
237
239
  return oci8_make_ocidate((OCIDate *)data);
240
+ case ATTR_TIMESTAMP:
241
+ return oci8_make_ocitimestamp(*(OCIDateTime**)data, FALSE);
242
+ case ATTR_TIMESTAMP_TZ:
243
+ return oci8_make_ocitimestamp(*(OCIDateTime**)data, TRUE);
238
244
  case ATTR_BINARY_DOUBLE:
239
245
  return rb_float_new(*(double*)data);
240
246
  case ATTR_BINARY_FLOAT:
@@ -413,6 +419,18 @@ static VALUE oci8_named_coll_set_coll_element(VALUE self, VALUE datatype, VALUE
413
419
  case ATTR_OCIDATE:
414
420
  cb_data.indp = &cb_data.ind;
415
421
  break;
422
+ case ATTR_TIMESTAMP:
423
+ chker2(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_TIMESTAMP, NULL, NULL, OCI_DURATION_SESSION, TRUE, &cb_data.data.ptr),
424
+ svcctx);
425
+ chker2(OCIObjectGetInd(oci8_envhp, oci8_errhp, cb_data.data.ptr, (dvoid**)&cb_data.indp),
426
+ svcctx);
427
+ break;
428
+ case ATTR_TIMESTAMP_TZ:
429
+ chker2(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_TIMESTAMP_TZ, NULL, NULL, OCI_DURATION_SESSION, TRUE, &cb_data.data.ptr),
430
+ svcctx);
431
+ chker2(OCIObjectGetInd(oci8_envhp, oci8_errhp, cb_data.data.ptr, (dvoid**)&cb_data.indp),
432
+ svcctx);
433
+ break;
416
434
  case ATTR_BINARY_DOUBLE:
417
435
  cb_data.data.dbl = 0.0;
418
436
  cb_data.indp = &cb_data.ind;
@@ -507,6 +525,8 @@ static VALUE set_coll_element_ensure(set_coll_element_cb_data_t *cb_data)
507
525
  switch (FIX2INT(datatype)) {
508
526
  case ATTR_STRING:
509
527
  case ATTR_RAW:
528
+ case ATTR_TIMESTAMP:
529
+ case ATTR_TIMESTAMP_TZ:
510
530
  case ATTR_NAMED_TYPE:
511
531
  case ATTR_NAMED_COLLECTION:
512
532
  if (cb_data->data.ptr != NULL) {
@@ -558,6 +578,12 @@ static void set_attribute(VALUE self, VALUE datatype, VALUE typeinfo, void *data
558
578
  case ATTR_OCIDATE:
559
579
  oci8_set_ocidate((OCIDate*)data, val);
560
580
  break;
581
+ case ATTR_TIMESTAMP:
582
+ oci8_set_ocitimestamp_tz(*(OCIDateTime **)data, val, Qnil);
583
+ break;
584
+ case ATTR_TIMESTAMP_TZ:
585
+ oci8_set_ocitimestamp_tz(*(OCIDateTime **)data, val, Qnil);
586
+ break;
561
587
  case ATTR_BINARY_DOUBLE:
562
588
  *(double*)data = NUM2DBL(val);
563
589
  break;
@@ -818,6 +844,10 @@ void Init_oci_object(VALUE cOCI8)
818
844
  /* @private */
819
845
  rb_define_const(cOCI8TDO, "ATTR_OCIDATE", INT2FIX(ATTR_OCIDATE));
820
846
  /* @private */
847
+ rb_define_const(cOCI8TDO, "ATTR_TIMESTAMP", INT2FIX(ATTR_TIMESTAMP));
848
+ /* @private */
849
+ rb_define_const(cOCI8TDO, "ATTR_TIMESTAMP_TZ", INT2FIX(ATTR_TIMESTAMP_TZ));
850
+ /* @private */
821
851
  rb_define_const(cOCI8TDO, "ATTR_BINARY_DOUBLE", INT2FIX(ATTR_BINARY_DOUBLE));
822
852
  /* @private */
823
853
  rb_define_const(cOCI8TDO, "ATTR_BINARY_FLOAT", INT2FIX(ATTR_BINARY_FLOAT));
data/ext/oci8/oci8.c CHANGED
@@ -582,6 +582,8 @@ static VALUE oci8_server_attach(VALUE self, VALUE dbname, VALUE attach_mode)
582
582
  static VALUE oci8_session_begin(VALUE self, VALUE cred, VALUE mode)
583
583
  {
584
584
  oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
585
+ char buf[100];
586
+ ub4 version;
585
587
 
586
588
  if (svcctx->logoff_strategy != &complex_logoff) {
587
589
  rb_raise(rb_eRuntimeError, "Use this method only for the service context handle created by OCI8#server_handle().");
@@ -604,6 +606,16 @@ static VALUE oci8_session_begin(VALUE self, VALUE cred, VALUE mode)
604
606
  oci8_errhp),
605
607
  &svcctx->base);
606
608
  svcctx->state |= OCI8_STATE_SESSION_BEGIN_WAS_CALLED;
609
+ if (have_OCIServerRelease2) {
610
+ chker2(OCIServerRelease2(svcctx->base.hp.ptr, oci8_errhp, (text*)buf,
611
+ sizeof(buf), (ub1)svcctx->base.type, &version, OCI_DEFAULT),
612
+ &svcctx->base);
613
+ } else {
614
+ chker2(OCIServerRelease(svcctx->base.hp.ptr, oci8_errhp, (text*)buf,
615
+ sizeof(buf), (ub1)svcctx->base.type, &version),
616
+ &svcctx->base);
617
+ }
618
+ svcctx->server_version = version;
607
619
  return Qnil;
608
620
  }
609
621
 
@@ -796,11 +808,8 @@ static VALUE oci8_break(VALUE self)
796
808
  static VALUE oci8_oracle_server_vernum(VALUE self)
797
809
  {
798
810
  oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
799
- char buf[100];
800
- ub4 version;
801
811
 
802
- chker2(OCIServerRelease(svcctx->base.hp.ptr, oci8_errhp, (text*)buf, sizeof(buf), (ub1)svcctx->base.type, &version), &svcctx->base);
803
- return UINT2NUM(version);
812
+ return UINT2NUM(svcctx->server_version);
804
813
  }
805
814
 
806
815
  /*
data/ext/oci8/oci8.h CHANGED
@@ -21,8 +21,23 @@ extern "C"
21
21
  }
22
22
  #endif
23
23
 
24
+ /*
25
+ * Oracle version number format in 32-bit integer.
26
+ *
27
+ * Oracle 12c or earier Oracle 18c or later
28
+ *
29
+ * hexadecimal -> dotted version number hexadecimal -> dotted version number
30
+ * 0c102304 -> 12.1.2.3.4 12012034 -> 18.1.2.3.4
31
+ * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
32
+ * 0c ------------' | | | | 2 bytes 12 ------------' | | | | 2 bytes
33
+ * 1 --------------' | | | 1 byte 01 -------------' | | | 2 bytes
34
+ * 02 --------------' | | 2 bytes 2 --------------' | | 1 byte
35
+ * 3 ---------------' | 1 byte 03 --------------' | 2 bytes
36
+ * 04 ---------------' 2 bytes 4 ---------------' 1 byte
37
+ */
24
38
  #define ORAVERNUM(major, minor, update, patch, port_update) \
25
- (((major) << 24) | ((minor) << 20) | ((update) << 12) | ((patch) << 8) | (port_update))
39
+ (((major) >= 18) ? (((major) << 24) | ((minor) << 16) | ((update) << 12) | ((patch) << 4) | (port_update)) \
40
+ : (((major) << 24) | ((minor) << 20) | ((update) << 12) | ((patch) << 8) | (port_update)))
26
41
 
27
42
  #define ORAVER_8_0 ORAVERNUM(8, 0, 0, 0, 0)
28
43
  #define ORAVER_8_1 ORAVERNUM(8, 1, 0, 0, 0)
@@ -32,6 +47,7 @@ extern "C"
32
47
  #define ORAVER_10_2 ORAVERNUM(10, 2, 0, 0, 0)
33
48
  #define ORAVER_11_1 ORAVERNUM(11, 1, 0, 0, 0)
34
49
  #define ORAVER_12_1 ORAVERNUM(12, 1, 0, 0, 0)
50
+ #define ORAVER_18 ORAVERNUM(18, 0, 0, 0, 0)
35
51
 
36
52
  #include "extconf.h"
37
53
  #include <ruby/encoding.h>
@@ -316,6 +332,7 @@ typedef struct oci8_svcctx {
316
332
  const oci8_logoff_strategy_t *logoff_strategy;
317
333
  OCISession *usrhp;
318
334
  OCIServer *srvhp;
335
+ ub4 server_version;
319
336
  rb_pid_t pid;
320
337
  unsigned char state;
321
338
  char is_autocommit;
@@ -489,8 +506,8 @@ OCINumber *oci8_dbl_to_onum(OCINumber *result, double dbl, OCIError *errhp);
489
506
  void Init_oci_datetime(void);
490
507
  VALUE oci8_make_ocidate(OCIDate *od);
491
508
  OCIDate *oci8_set_ocidate(OCIDate *od, VALUE val);
492
- VALUE oci8_make_ocidatetime(OCIDateTime *dttm);
493
- OCIDateTime *oci8_set_ocidatetime(OCIDateTime *dttm, VALUE val);
509
+ VALUE oci8_make_ocitimestamp(OCIDateTime *dttm, boolean have_tz);
510
+ OCIDateTime *oci8_set_ocitimestamp_tz(OCIDateTime *dttm, VALUE val, VALUE svc);
494
511
  VALUE oci8_make_interval_ym(OCIInterval *s);
495
512
  VALUE oci8_make_interval_ds(OCIInterval *s);
496
513
 
data/ext/oci8/oci8lib.c CHANGED
@@ -620,11 +620,13 @@ void *oci8_find_symbol(const char *symbol_name)
620
620
  "libclntsh.sl.10.1",
621
621
  #elif defined(__APPLE__)
622
622
  /* Mac OS X */
623
+ "libclntsh.dylib",
623
624
  "libclntsh.dylib.12.1",
624
625
  "libclntsh.dylib.11.1",
625
626
  "libclntsh.dylib.10.1",
626
627
  #else
627
628
  /* Linux, Solaris and HP-UX(IA64) */
629
+ "libclntsh.so",
628
630
  "libclntsh.so.12.1",
629
631
  "libclntsh.so.11.1",
630
632
  "libclntsh.so.10.1",
data/ext/oci8/win32.c CHANGED
@@ -21,26 +21,6 @@
21
21
  * @private
22
22
  */
23
23
 
24
- NORETURN(static void raise_error(void));
25
-
26
- static void raise_error(void)
27
- {
28
- char msg[1024];
29
- int err = GetLastError();
30
- char *p;
31
-
32
- sprintf(msg, "%d: ", err);
33
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
34
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
35
- msg + strlen(msg), sizeof(msg) - strlen(msg), NULL);
36
- for (p = msg; *p != '\0'; p++) {
37
- if (*p == '\n' || *p == '\r') {
38
- *p = ' ';
39
- }
40
- }
41
- rb_raise(rb_eRuntimeError, "%s", msg);
42
- }
43
-
44
24
  typedef struct {
45
25
  HKEY hKey;
46
26
  HKEY hSubKey;
data/lib/oci8.rb CHANGED
@@ -140,6 +140,8 @@ class OCI8
140
140
  ORAVER_11_1 = OCI8::OracleVersion.new(11, 1)
141
141
  # @private
142
142
  ORAVER_12_1 = OCI8::OracleVersion.new(12, 1)
143
+ # @private
144
+ ORAVER_18 = OCI8::OracleVersion.new(18)
143
145
 
144
146
  # @private
145
147
  @@oracle_client_version = OCI8::OracleVersion.new(self.oracle_client_vernum)
data/lib/oci8/metadata.rb CHANGED
@@ -1503,7 +1503,11 @@ class OCI8
1503
1503
  attr_get_sb1(OCI_ATTR_SCALE)
1504
1504
  end
1505
1505
 
1506
- # The datatype levels. This attribute always returns zero.
1506
+ # The nest level.
1507
+ #
1508
+ # Oracle manual says that it always returns zero. However it returns
1509
+ # the depth of {OCI8::ArgBase#arguments} calls when #arguments returns
1510
+ # a non-empty array.
1507
1511
  def level
1508
1512
  attr_get_ub2(OCI_ATTR_LEVEL)
1509
1513
  end
@@ -1607,6 +1611,10 @@ class OCI8
1607
1611
 
1608
1612
  # The list of arguments at the next level (when the argument is
1609
1613
  # of a record or table type).
1614
+ #
1615
+ # This method returns an array containing type information when
1616
+ # the type is a user-defined type and the Oracle server version
1617
+ # is 12c or earlier. Otherwise, it returns an empty array.
1610
1618
  def arguments
1611
1619
  @arguments ||= list_arguments.to_a
1612
1620
  end
data/lib/oci8/object.rb CHANGED
@@ -493,6 +493,16 @@ EOS
493
493
  Proc.new do |val| datetime_to_array(val, :date) end, # set_proc
494
494
  Proc.new do |val| array_to_time(val, :local) end, # get_proc
495
495
  ]
496
+ when :timestamp
497
+ [ATTR_TIMESTAMP, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
498
+ Proc.new do |val| datetime_to_array(val, :timestamp) end, # set_proc
499
+ Proc.new do |val| array_to_time(val, :local) end, # get_proc
500
+ ]
501
+ when :timestamp_tz
502
+ [ATTR_TIMESTAMP_TZ, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
503
+ Proc.new do |val| datetime_to_array(val, :timestamp_tz) end, # set_proc
504
+ Proc.new do |val| array_to_time(val, nil) end, # get_proc
505
+ ]
496
506
  when :binary_double
497
507
  [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
498
508
  when :binary_float
data/lib/oci8/oci8.rb CHANGED
@@ -340,6 +340,12 @@ class OCI8
340
340
 
341
341
  # Returns the Oracle server version.
342
342
  #
343
+ # When the Oracle client version is 12c or earlier and
344
+ # the Oracle server version is 18c or later, this method
345
+ # doesn't return *full* version number such as '18.3.0.0.0'.
346
+ # It returns version number whose number components after
347
+ # the first dot are zeros such as '18.0.0.0.0'.
348
+ #
343
349
  # @see OCI8.oracle_client_version
344
350
  # @return [OCI8::OracleVersion]
345
351
  def oracle_server_version
@@ -66,6 +66,12 @@ class OCI8
66
66
  major, minor, update, patch, port_update = arg.split('.').collect do |v|
67
67
  v.to_i
68
68
  end
69
+ elsif arg >= 0x12000000
70
+ major = (arg & 0xFF000000) >> 24
71
+ minor = (arg & 0x00FF0000) >> 16
72
+ update = (arg & 0x0000F000) >> 12
73
+ patch = (arg & 0x00000FF0) >> 4
74
+ port_update = (arg & 0x0000000F)
69
75
  elsif arg >= 0x08000000
70
76
  major = (arg & 0xFF000000) >> 24
71
77
  minor = (arg & 0x00F00000) >> 20
@@ -80,7 +86,11 @@ class OCI8
80
86
  @update = update || 0
81
87
  @patch = patch || 0
82
88
  @port_update = port_update || 0
83
- @vernum = (@major << 24) | (@minor << 20) | (@update << 12) | (@patch << 8) | @port_update
89
+ @vernum = if @major >= 18
90
+ (@major << 24) | (@minor << 16) | (@update << 12) | (@patch << 4) | @port_update
91
+ else
92
+ (@major << 24) | (@minor << 20) | (@update << 12) | (@patch << 8) | @port_update
93
+ end
84
94
  end
85
95
 
86
96
  # Compares +self+ and +other+.
data/lib/oci8/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class OCI8
2
- VERSION = "2.2.5.1"
2
+ VERSION = "2.2.6"
3
3
  end
data/lib/ruby-oci8.rb CHANGED
@@ -1,4 +1 @@
1
- if caller[0] !~ /\/bundler\/runtime\.rb:\d+:in `require'/
2
- warn "Don't require 'ruby-oci8'. Use \"require 'oci8'\" instead. 'ruby-oci8.rb' was added only for 'Bundler.require'."
3
- end
4
1
  require 'oci8'
@@ -61,6 +61,8 @@ create type rb_test_obj as object (
61
61
  obj_array_val rb_test_obj_elem_array,
62
62
  obj_ary_of_ary_val rb_test_obj_elem_ary_of_ary,
63
63
  date_val date,
64
+ timestamp_val timestamp(9),
65
+ timestamp_tz_val timestamp(9) with time zone,
64
66
  -- date_array_val rb_test_date_array,
65
67
 
66
68
  constructor function rb_test_obj(n number) return self as result,
@@ -72,19 +74,23 @@ create type rb_test_obj as object (
72
74
  member procedure member_proc(n in integer)
73
75
  )
74
76
  /
75
- create or replace type body rb_test_obj is
76
-
77
+ create type body rb_test_obj is
77
78
  constructor function rb_test_obj(n number) return self as result is
78
- function to_test_date(n number) return date is
79
- begin
80
- return to_date(to_char(1990 + n, 'FM0000') ||
81
- to_char(mod(round(n) * 5, 12) + 1, 'FM00') ||
82
- to_char(mod(round(n) * 7, 27) + 1, 'FM00') ||
83
- to_char(mod(round(n) * 9, 24), 'FM00') ||
84
- to_char(mod(round(n) * 11, 60), 'FM00') ||
85
- to_char(mod(round(n) * 13, 60), 'FM00'), 'yyyymmddhh24miss');
86
- end;
79
+ str varchar(28);
80
+ ts timestamp(9);
81
+ ts_tz timestamp(9) with time zone;
87
82
  begin
83
+ str := to_char(1990 + n, 'FM0000') ||
84
+ to_char(mod(round(n) * 5, 12) + 1, 'FM00') ||
85
+ to_char(mod(round(n) * 7, 27) + 1, 'FM00') ||
86
+ to_char(mod(round(n) * 9, 24), 'FM00') ||
87
+ to_char(mod(round(n) * 11, 60), 'FM00') ||
88
+ to_char(mod(round(n) * 13, 60), 'FM00') ||
89
+ to_char(mod(round(n) * 333333333, 1000000000), 'FM000000000');
90
+ ts := to_timestamp(str, 'yyyymmddhh24missff9');
91
+ str := str || to_char(mod(round(n) * 15, 24) - 11, 'FMS00') ||
92
+ to_char(mod(round(n) * 17, 60), 'FM00');
93
+ ts_tz := to_timestamp_tz(str, 'yyyymmddhh24missff9tzhtzm');
88
94
  self.int_val := n;
89
95
  self.flt_val := n;
90
96
  self.num_val := n;
@@ -96,7 +102,9 @@ create or replace type body rb_test_obj is
96
102
  self.nclob_val := to_clob(n);
97
103
  self.blob_val := to_blob(utl_raw.cast_to_raw(to_char(n)));
98
104
  self.obj_val := rb_test_obj_elem(n, n + 1);
99
- self.date_val := to_test_date(n);
105
+ self.date_val := ts;
106
+ self.timestamp_val := ts;
107
+ self.timestamp_tz_val := ts_tz;
100
108
  if self.int_val != 1 then
101
109
  self.int_array_val := rb_test_int_array(n, n + 1, n + 2);
102
110
  self.flt_array_val := rb_test_flt_array(n, n + 1, n + 2);
@@ -120,7 +128,7 @@ create or replace type body rb_test_obj is
120
128
 
121
129
  static function test_object_version return integer is
122
130
  begin
123
- return 3;
131
+ return 4;
124
132
  end;
125
133
 
126
134
  static function class_func(n number) return rb_test_obj is
data/test/test_all.rb CHANGED
@@ -26,6 +26,7 @@ require "#{srcdir}/test_oracle_version"
26
26
  require "#{srcdir}/test_error"
27
27
  require "#{srcdir}/test_connection_pool"
28
28
  require "#{srcdir}/test_object"
29
+ require "#{srcdir}/test_properties.rb"
29
30
 
30
31
  if OCI8.respond_to? :encoding
31
32
  require "#{srcdir}/test_encoding"
data/test/test_clob.rb CHANGED
@@ -24,22 +24,6 @@ class TestCLob < Minitest::Test
24
24
  lob.close
25
25
  end
26
26
 
27
- def test_insert_with_flush
28
- filename = File.basename($lobfile)
29
- @conn.exec("DELETE FROM test_table WHERE filename = :1", filename)
30
- @conn.exec("INSERT INTO test_table(filename, content) VALUES (:1, EMPTY_CLOB())", filename)
31
- cursor = @conn.exec("SELECT content FROM test_table WHERE filename = :1 FOR UPDATE", filename)
32
- lob = cursor.fetch[0]
33
- lob.sync = false
34
- open($lobfile) do |f|
35
- while s = f.read(1000)
36
- lob.write(s)
37
- end
38
- end
39
- lob.flush
40
- lob.close
41
- end
42
-
43
27
  def test_insert_symbol
44
28
  filename = 'test_symbol'
45
29
  value = :foo_bar
@@ -76,6 +60,9 @@ class TestCLob < Minitest::Test
76
60
 
77
61
  # https://github.com/kubo/ruby-oci8/issues/20
78
62
  def test_github_issue_20
63
+ # Skip this test if FULLTEST isn't set because it takes 4 minutes in my Linux box.
64
+ return if ENV['FULLTEST']
65
+
79
66
  lob1 = OCI8::CLOB.new(@conn, ' ' * (1024 * 1024))
80
67
  lob1.read(1) # to suppress `warning: assigned but unused variable - lob1`
81
68
  begin
data/test/test_object.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  require 'oci8'
2
2
  require File.dirname(__FILE__) + '/config'
3
3
 
4
+ class Time
5
+ def inspect
6
+ self.strftime("%Y-%m-%d %H:%M:%S.%N %:z")
7
+ end
8
+ end
9
+
4
10
  conn = OCI8.new($dbuser, $dbpass, $dbname)
5
11
  error_message = nil
6
12
  begin
@@ -22,7 +28,7 @@ begin
22
28
 
23
29
  begin
24
30
  version = RbTestObj.test_object_version(conn)
25
- error_message = "Invalid test object version" if version != 3
31
+ error_message = "Invalid test object version" if version != 4
26
32
  rescue NoMethodError
27
33
  raise unless $!.to_s.include?('test_object_version')
28
34
  error_message = "rb_test_obj.test_object_version is not declared."
@@ -100,7 +106,8 @@ class TestObj1 < Minitest::Test
100
106
  attr_reader :obj_array_val
101
107
  attr_reader :obj_ary_of_ary_val
102
108
  attr_reader :date_val
103
- # attr_reader :date_array_val
109
+ attr_reader :timestamp_val
110
+ attr_reader :timestamp_tz_val
104
111
 
105
112
  attr_accessor :assertions
106
113
 
@@ -109,16 +116,28 @@ class TestObj1 < Minitest::Test
109
116
  @assertions = 0
110
117
  end
111
118
 
112
- def to_test_date(n)
119
+ def to_test_datetime(n, type)
113
120
  year = (1990 + n).round
114
121
  month = (n.round * 5) % 12 + 1
115
122
  mday = (n.round * 7) % 27 + 1
116
123
  hour = (n.round * 9) % 24
117
124
  minute = (n.round * 11) % 60
118
125
  sec = (n.round * 13) % 60
119
- convert_to_time(year, month, mday, hour, minute, sec, 0, nil)
126
+ nsec = if type == :date
127
+ 0
128
+ else
129
+ ((n.round * 333_333_333) % 1_000_000_000).to_r / 1_000_000_000
130
+ end
131
+ tz = if type == :timestamp_tz
132
+ tzh = (n.round * 15) % 24 - 11
133
+ tzm = (n.round * 17) % 60
134
+ format('%+03d:%02d', tzh, tzm)
135
+ else
136
+ nil
137
+ end
138
+ convert_to_time(year, month, mday, hour, minute, sec, nsec, tz)
120
139
  end
121
- private :to_test_date
140
+ private :to_test_datetime
122
141
 
123
142
  def next
124
143
  @n += 1.2
@@ -135,7 +154,9 @@ class TestObj1 < Minitest::Test
135
154
  @nclob_val = @str_val
136
155
  @blob_val = @str_val
137
156
  @obj_val = ExpectedValObjElem.new(@int_val, @int_val + 1)
138
- @date_val = to_test_date(@n)
157
+ @date_val = to_test_datetime(@n, :date)
158
+ @timestamp_val = to_test_datetime(@n, :timestamp)
159
+ @timestamp_tz_val = to_test_datetime(@n, :timestamp_tz)
139
160
  if @int_val == 1
140
161
  @int_array_val = nil
141
162
  @flt_array_val = nil
@@ -199,7 +220,8 @@ class TestObj1 < Minitest::Test
199
220
  obj_array_val = val[18]
200
221
  obj_ary_of_ary_val = val[19]
201
222
  date_val = val[20]
202
- # date_array_val = val[18]
223
+ timestamp_val = val[21]
224
+ timestamp_tz_val = val[22]
203
225
  else
204
226
  assert_instance_of(RbTestObj, val)
205
227
  int_val = val.int_val
@@ -223,7 +245,8 @@ class TestObj1 < Minitest::Test
223
245
  obj_array_val = val.obj_array_val
224
246
  obj_ary_of_ary_val = val.obj_ary_of_ary_val
225
247
  date_val = val.date_val
226
- # date_array_val = val.date_array_val
248
+ timestamp_val = val.timestamp_val
249
+ timestamp_tz_val = val.timestamp_tz_val
227
250
  end
228
251
 
229
252
  assert_equal(@int_val, int_val)
@@ -267,7 +290,8 @@ class TestObj1 < Minitest::Test
267
290
  assert_nil(obj_ary_of_ary_val)
268
291
  end
269
292
  assert_equal(@date_val, date_val)
270
- # assert_equal(@date_array_val, date_array_val && date_array_val.to_ary)
293
+ assert_equal(@timestamp_val, timestamp_val)
294
+ assert_equal(@timestamp_tz_val, timestamp_tz_val)
271
295
  end
272
296
 
273
297
  def assert_array_in_delta(exp, val)
data/test/test_oci8.rb CHANGED
@@ -491,9 +491,23 @@ EOS
491
491
  else
492
492
  raise "Unknown column size #{column_size}"
493
493
  end
494
- driver_name = cursor.fetch[0]
494
+ driver_name = cursor.fetch[0].strip
495
495
  cursor.close
496
496
  assert_equal(expected_value, driver_name)
497
497
  end
498
498
  end
499
+
500
+ def test_server_version
501
+ cursor = @conn.exec("select * from product_component_version where product like 'Oracle Database %'")
502
+ row = cursor.fetch_hash
503
+ cursor.close
504
+ ver = if OCI8::oracle_client_version >= OCI8::ORAVER_18
505
+ row['VERSION_FULL'] || row['VERSION']
506
+ else
507
+ # OCI8#oracle_server_version could not get infomation corresponding
508
+ # to VERSION_FULL when the Oracle client version is below 18.1.
509
+ row['VERSION']
510
+ end
511
+ assert_equal(ver, @conn.oracle_server_version.to_s)
512
+ end
499
513
  end # TestOCI8
@@ -639,7 +639,11 @@ class TestPackageType < Minitest::Test
639
639
  :sub_name => "TABLE_OF_PLS_INTEGER",
640
640
  :link => "",
641
641
  #:type_metadata => nil,
642
- :arguments => {
642
+ :arguments => ($oracle_server_version >= OCI8::ORAVER_18) ?
643
+ {
644
+ :class => Array,
645
+ :size => 0,
646
+ } : {
643
647
  :class => Array,
644
648
  :size => 1,
645
649
  [0] => {
@@ -729,7 +733,11 @@ class TestPackageType < Minitest::Test
729
733
  :sub_name => "TABLE_OF_REC1",
730
734
  :link => "",
731
735
  #:type_metadata => nil,
732
- :arguments => {
736
+ :arguments => ($oracle_server_version >= OCI8::ORAVER_18) ?
737
+ {
738
+ :class => Array,
739
+ :size => 0,
740
+ } : {
733
741
  :class => Array,
734
742
  :size => 1,
735
743
  [0] => {
@@ -846,7 +854,11 @@ class TestPackageType < Minitest::Test
846
854
  :sub_name => "TABLE_OF_REC1",
847
855
  :link => "",
848
856
  #:type_metadata => nil,
849
- :arguments => {
857
+ :arguments => ($oracle_server_version >= OCI8::ORAVER_18) ?
858
+ {
859
+ :class => Array,
860
+ :size => 0,
861
+ } : {
850
862
  :class => Array,
851
863
  :size => 1,
852
864
  [0] => {
@@ -0,0 +1,17 @@
1
+ require 'oci8'
2
+ require File.dirname(__FILE__) + '/config'
3
+
4
+ class TestProperties < Minitest::Test
5
+ def test_tcp_keepalive_time
6
+ begin
7
+ oldval = OCI8.properties[:tcp_keepalive_time]
8
+ begin
9
+ OCI8.properties[:tcp_keepalive_time] = 600
10
+ assert(true)
11
+ ensure
12
+ OCI8.properties[:tcp_keepalive_time] = oldval
13
+ end
14
+ rescue NotImplementedError
15
+ end
16
+ end
17
+ end
metadata CHANGED
@@ -1,35 +1,27 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ruby-oci8
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 2
7
- - 2
8
- - 5
9
- - 1
10
- version: 2.2.5.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.2.6
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Kubo Takehiro
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2017-12-27 00:00:00 +09:00
19
- default_executable:
12
+ date: 2018-08-22 00:00:00.000000000 Z
20
13
  dependencies: []
14
+ description: ! 'ruby-oci8 is a ruby interface for Oracle using OCI8 API. It is available
15
+ with Oracle 10g or later including Oracle Instant Client.
21
16
 
22
- description: |
23
- ruby-oci8 is a ruby interface for Oracle using OCI8 API. It is available with Oracle 10g or later including Oracle Instant Client.
24
-
17
+ '
25
18
  email: kubo@jiubao.org
26
19
  executables: []
27
-
28
- extensions:
20
+ extensions:
29
21
  - ext/oci8/extconf.rb
30
- extra_rdoc_files:
22
+ extra_rdoc_files:
31
23
  - README.md
32
- files:
24
+ files:
33
25
  - .yardopts
34
26
  - COPYING
35
27
  - COPYING_old
@@ -141,41 +133,34 @@ files:
141
133
  - test/test_oradate.rb
142
134
  - test/test_oranumber.rb
143
135
  - test/test_package_type.rb
136
+ - test/test_properties.rb
144
137
  - test/test_rowid.rb
145
- has_rdoc: true
146
138
  homepage: http://www.rubydoc.info/github/kubo/ruby-oci8
147
- licenses:
139
+ licenses:
148
140
  - BSD-2-Clause
149
141
  post_install_message:
150
142
  rdoc_options: []
151
-
152
- require_paths:
143
+ require_paths:
153
144
  - lib
154
145
  - ext/oci8
155
- required_ruby_version: !ruby/object:Gem::Requirement
146
+ required_ruby_version: !ruby/object:Gem::Requirement
156
147
  none: false
157
- requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- segments:
161
- - 1
162
- - 9
163
- - 1
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
164
151
  version: 1.9.1
165
- required_rubygems_version: !ruby/object:Gem::Requirement
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
153
  none: false
167
- requirements:
168
- - - ">="
169
- - !ruby/object:Gem::Version
170
- segments:
171
- - 0
172
- version: "0"
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
173
158
  requirements: []
174
-
175
159
  rubyforge_project:
176
- rubygems_version: 1.3.7
160
+ rubygems_version: 1.8.11
177
161
  signing_key:
178
162
  specification_version: 3
179
163
  summary: Ruby interface for Oracle using OCI8 API
180
- test_files:
164
+ test_files:
181
165
  - test/test_all.rb
166
+ has_rdoc: yard