embulk-output-oracle 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -3
  3. data/build.gradle +3 -0
  4. data/classpath/{embulk-output-jdbc-0.2.1.jar → embulk-output-jdbc-0.2.2.jar} +0 -0
  5. data/classpath/embulk-output-oracle-0.2.2.jar +0 -0
  6. data/lib/embulk/linux_x64/libembulk-output-oracle.so +0 -0
  7. data/lib/embulk/win_x64/embulk-output-oracle.dll +0 -0
  8. data/src/main/cpp/common/dir-path-load.cpp +424 -0
  9. data/src/main/cpp/common/dir-path-load.h +36 -0
  10. data/src/main/cpp/common/embulk-output-oracle.cpp +196 -0
  11. data/src/main/cpp/common/org_embulk_output_oracle_oci_OCI.h +77 -0
  12. data/src/main/cpp/linux/build.sh +21 -0
  13. data/src/main/cpp/win/build.bat +32 -0
  14. data/src/main/cpp/win/dllmain.cpp +25 -0
  15. data/src/main/cpp/win/embulk-output-oracle.sln +39 -0
  16. data/src/main/cpp/win/embulk-output-oracle.vcxproj +176 -0
  17. data/src/main/java/org/embulk/output/OracleOutputPlugin.java +51 -10
  18. data/src/main/java/org/embulk/output/oracle/DirectBatchInsert.java +289 -0
  19. data/src/main/java/org/embulk/output/oracle/InsertMethod.java +8 -0
  20. data/src/main/java/org/embulk/output/oracle/OracleCharset.java +19 -0
  21. data/src/main/java/org/embulk/output/oracle/OracleOutputConnection.java +60 -1
  22. data/src/main/java/org/embulk/output/oracle/OracleOutputConnector.java +7 -5
  23. data/src/main/java/org/embulk/output/oracle/TimestampFormat.java +37 -0
  24. data/src/main/java/org/embulk/output/oracle/oci/ColumnDefinition.java +26 -0
  25. data/src/main/java/org/embulk/output/oracle/oci/OCI.java +139 -0
  26. data/src/main/java/org/embulk/output/oracle/oci/OCIManager.java +64 -0
  27. data/src/main/java/org/embulk/output/oracle/oci/OCIWrapper.java +96 -0
  28. data/src/main/java/org/embulk/output/oracle/oci/RowBuffer.java +99 -0
  29. data/src/main/java/org/embulk/output/oracle/oci/TableDefinition.java +24 -0
  30. data/src/main/java/org/embulk/output/oracle/setter/OracleColumnSetterFactory.java +14 -14
  31. data/src/test/cpp/common/embulk-output-oracle-test.cpp +69 -0
  32. data/src/test/cpp/linux/build.sh +19 -0
  33. data/src/test/cpp/win/build.bat +29 -0
  34. data/src/test/cpp/win/embulk-output-oracle-test.vcxproj +155 -0
  35. data/src/test/java/org/embulk/input/filesplit/LocalFileSplitInputPlugin.java +187 -0
  36. data/src/test/java/org/embulk/input/filesplit/PartialFile.java +50 -0
  37. data/src/test/java/org/embulk/input/filesplit/PartialFileInputStream.java +154 -0
  38. data/src/test/java/org/embulk/output/oracle/ChildFirstClassLoader.java +42 -0
  39. data/src/test/java/org/embulk/output/oracle/EmbulkPluginTester.java +120 -0
  40. data/src/test/java/org/embulk/output/oracle/EmptyConfigSource.java +100 -0
  41. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java +161 -0
  42. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTestImpl.java +413 -0
  43. data/src/test/java/org/embulk/output/oracle/TimestampFormatTest.java +57 -0
  44. data/src/test/resources/data/test1/test1.csv +3 -0
  45. data/src/test/resources/yml/test-insert-direct.yml +26 -0
  46. data/src/test/resources/yml/test-insert-oci-split.yml +26 -0
  47. data/src/test/resources/yml/test-insert-oci.yml +26 -0
  48. data/src/test/resources/yml/test-insert.yml +25 -0
  49. data/src/test/resources/yml/test-replace-long-name.yml +25 -0
  50. data/src/test/resources/yml/test-replace.yml +25 -0
  51. data/src/test/resources/yml/test-url.yml +24 -0
  52. metadata +46 -4
  53. data/classpath/embulk-output-oracle-0.2.1.jar +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc81277bd2a1f2ae80924bb543d6eecfcd3d4d6c
4
- data.tar.gz: 3b0b5fa66bc6429a37b1ce15bd3a1d9ea4aca237
3
+ metadata.gz: 851ec609b8d3f58c4138db775874f849b549eba6
4
+ data.tar.gz: 8cb8c62a8dac65d4beba6d5b80ac38d3a9843446
5
5
  SHA512:
6
- metadata.gz: 215e1945daf0792754efdfb7c6adc1596d9a020a8dd53584367be82f9a9d44397a77ae42cbd2601f78b1290e4d2afab22a05c9c6a3fd6e47e6c900dad3484231
7
- data.tar.gz: 4078b17328554d4abe9bbfa89aab60d109fe6acd29c7b1c1011068375a6e879a7afe6b4c18ac34b4b0e5581be2a875dc4485c320f8aa032362543362b8e762f3
6
+ metadata.gz: 2fb3a808befee8d227180385bbd9d0ca3d1ebda757eed810d4be377db877507f77b2e3b73f5056160cf625c1e86c3b881061eb09259565f2cf588ffceb352f41
7
+ data.tar.gz: 3b8b1450b520fadcf284557a32f5e7127c9f6831221d3c88f9c3e542808abab400e7c1d9fedfde0d1fb4e1932681c1074d853857b535efdf7827e066691d76f2
data/README.md CHANGED
@@ -5,7 +5,7 @@ Oracle output plugins for Embulk loads records to Oracle.
5
5
  ## Overview
6
6
 
7
7
  * **Plugin type**: output
8
- * **Load all or nothing**: depnds on the mode:
8
+ * **Load all or nothing**: depends on the mode:
9
9
  * **insert**: no
10
10
  * **replace**: yes
11
11
  * **Resume supported**: no
@@ -13,17 +13,33 @@ Oracle output plugins for Embulk loads records to Oracle.
13
13
  ## Configuration
14
14
 
15
15
  - **driver_path**: path to the jar file of the Oracle JDBC driver (string)
16
- - **host**: database host name (string, required if url is not set)
16
+ - **host**: database host name (string, required if url is not set or insert_method is "oci")
17
17
  - **port**: database port number (integer, default: 1521)
18
18
  - **user**: database login user name (string, required)
19
19
  - **password**: database login password (string, default: "")
20
- - **database**: destination database name (string, required if url is not set)
20
+ - **database**: destination database name (string, required if url is not set or insert_method is "oci")
21
21
  - **url**: URL of the JDBC connection (string, optional)
22
22
  - **table**: destination table name (string, required)
23
23
  - **mode**: "replace" or "insert" (string, required)
24
+ - **insert_method**: see below
24
25
  - **batch_size**: size of a single batch insert (integer, default: 16777216)
25
26
  - **options**: extra connection properties (hash, default: {})
26
27
 
28
+ insert_method supports three options.
29
+
30
+ "normal" means normal insert (default). It requires Oracle JDBC driver.
31
+
32
+ "direct" means direct path insert. It is faster than 'normal.
33
+ It requires Oracle JDBC driver too, but the version 12 driver doesn't work (the version 11 driver works).
34
+
35
+ "oci" means direct path insert using OCI(Oracle Call Interface). It is fastest.
36
+ It requires both Oracle JDBC driver and Oracle Instant Client (version 12.1.0.2.0).
37
+ You must set the library loading path to the OCI library.
38
+
39
+ If you use "oci", platform dependent library written in cpp is required.
40
+ Windows(x64) library and Linux(x64) are bundled, but others are not bundled.
41
+ You should build by yourself and set the library loading path to it.
42
+
27
43
 
28
44
  ### Example
29
45
 
@@ -37,6 +53,7 @@ out:
37
53
  database: my_database
38
54
  table: my_table
39
55
  mode: insert
56
+ insert_method: direct
40
57
  ```
41
58
 
42
59
  ### Build
@@ -44,3 +61,7 @@ out:
44
61
  ```
45
62
  $ ./gradlew gem
46
63
  ```
64
+
65
+ For Windows(x64), you can build cpp library by "src/main/cpp/win/build.bat".
66
+
67
+ For Linux, you can build cpp library by "src/main/cpp/linux/build.sh".
data/build.gradle CHANGED
@@ -1,3 +1,6 @@
1
+ [compileTestJava]*.options*.encoding = 'UTF-8'
2
+
1
3
  dependencies {
2
4
  compile project(':embulk-output-jdbc')
5
+ testCompile 'org.embulk:embulk-standards:0.5.5'
3
6
  }
@@ -0,0 +1,424 @@
1
+ #include <string.h>
2
+ #include <stdio.h>
3
+ #include <malloc.h>
4
+ #include "dir-path-load.h"
5
+
6
+ #pragma warning (disable: 4996)
7
+
8
+
9
+ static int check(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, const char* message, sword result)
10
+ {
11
+ strcpy(context->message, "");
12
+
13
+ if (result == OCI_ERROR) {
14
+ sprintf(context->message, "OCI : %s failed.", message);
15
+ sb4 errCode;
16
+ OraText text[512];
17
+ if (OCIErrorGet(context->err, 1, NULL, &errCode, text, sizeof(text) / sizeof(OraText), OCI_HTYPE_ERROR) != OCI_SUCCESS) {
18
+ strcat(context->message, " OCIErrorGet failed.");
19
+ } else {
20
+ strcat(context->message, " ");
21
+ strncat(context->message, (const char*)text, sizeof(context->message) - strlen(context->message) - 1);
22
+ }
23
+ return OCI_ERROR;
24
+ }
25
+
26
+ if (result == OCI_INVALID_HANDLE) {
27
+ sprintf(context->message, "OCI : %s failed : invalid handle.", message);
28
+ return OCI_ERROR;
29
+ }
30
+
31
+ if (result == OCI_NO_DATA) {
32
+ sprintf(context->message, "OCI : %s failed : no data.", message);
33
+ return OCI_ERROR;
34
+ }
35
+
36
+ if (result != OCI_SUCCESS) {
37
+ sprintf(context->message, "OCI : %s failed : %d.", message, result);
38
+ return OCI_ERROR;
39
+ }
40
+
41
+ return OCI_SUCCESS;
42
+ }
43
+
44
+ void embulk_output_oracle_freeDirPathHandles(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context)
45
+ {
46
+ if (context->csv != NULL) fclose(context->csv);
47
+ if (context->buffer != NULL) free(context->buffer);
48
+ if (context->dpstr != NULL) OCIHandleFree(context->dpstr, OCI_HTYPE_DIRPATH_STREAM);
49
+ if (context->dpca != NULL) OCIHandleFree(context->dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY);
50
+ if (context->dp != NULL) OCIHandleFree(context->dp, OCI_HTYPE_DIRPATH_CTX);
51
+ if (context->svc != NULL) OCIHandleFree(context->svc, OCI_HTYPE_SVCCTX);
52
+ if (context->err != NULL) OCIHandleFree(context->err, OCI_HTYPE_ERROR);
53
+ if (context->env != NULL) OCIHandleFree(context->env, OCI_HTYPE_ENV);
54
+ }
55
+
56
+ int embulk_output_oracle_prepareDirPathCtx(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, const char *dbName, const char *userName, const char *password)
57
+ {
58
+ if (check(context, "OCIEnvCreate", OCIEnvCreate(&context->env,
59
+ OCI_THREADED|OCI_OBJECT,
60
+ (void *)0,
61
+ 0,
62
+ 0,
63
+ 0,
64
+ (size_t)0,
65
+ (void **)0))) {
66
+ return OCI_ERROR;
67
+ }
68
+
69
+ // error handle
70
+ if (check(context, "OCIHandleAlloc(OCI_HTYPE_ERROR)", OCIHandleAlloc(
71
+ context->env,
72
+ (void **)&context->err,
73
+ OCI_HTYPE_ERROR,
74
+ (size_t)0,
75
+ (void **)0))) {
76
+ return OCI_ERROR;
77
+ }
78
+
79
+ // service context
80
+ if (check(context, "OCIHandleAlloc(OCI_HTYPE_SVCCTX)", OCIHandleAlloc(
81
+ context->env,
82
+ (void **)&context->svc,
83
+ OCI_HTYPE_SVCCTX,
84
+ (size_t)0,
85
+ (void **)0))) {
86
+ return OCI_ERROR;
87
+ }
88
+
89
+ // logon
90
+ if (check(context, "OCILogon", OCILogon(context->env,
91
+ context->err,
92
+ &context->svc,
93
+ (const OraText*)userName,
94
+ (ub4)strlen(userName),
95
+ (const OraText*)password,
96
+ (ub4)strlen(password),
97
+ (const OraText*)dbName, // dbName should be defined in 'tnsnames.ora' or a form of "host:port/db"
98
+ (ub4)strlen(dbName)))) {
99
+ return OCI_ERROR;
100
+ }
101
+
102
+ // direct path context
103
+ if (check(context, "OCIHandleAlloc(OCI_HTYPE_DIRPATH_CTX)", OCIHandleAlloc(
104
+ context->env,
105
+ (void **)&context->dp,
106
+ OCI_HTYPE_DIRPATH_CTX,
107
+ (size_t)0,
108
+ (void **)0))) {
109
+ return OCI_ERROR;
110
+ }
111
+
112
+ return OCI_SUCCESS;
113
+ }
114
+
115
+ static int isValid(EMBULK_OUTPUT_ORACLE_OCI_COL_DEF &colDef) {
116
+ return colDef.type != 0;
117
+ }
118
+
119
+ int embulk_output_oracle_prepareDirPathStream(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, const char *tableName, short charsetId, EMBULK_OUTPUT_ORACLE_OCI_COL_DEF *colDefs) {
120
+ // load table name
121
+ if (check(context, "OCIAttrSet(OCI_ATTR_NAME)", OCIAttrSet(context->dp, OCI_HTYPE_DIRPATH_CTX, (void*)tableName, (ub4)strlen(tableName), OCI_ATTR_NAME, context->err))) {
122
+ return OCI_ERROR;
123
+ }
124
+
125
+ // need to set charset explicitly because database charset is not set by default.
126
+ ub2 tempCharsetId = charsetId;
127
+ if (check(context, "OCIAttrSet(OCI_ATTR_CHARSET_ID)", OCIAttrSet(context->dp, OCI_HTYPE_DIRPATH_CTX, (void*)&tempCharsetId, sizeof(ub2), OCI_ATTR_CHARSET_ID, context->err))) {
128
+ return OCI_ERROR;
129
+ }
130
+
131
+ ub2 cols;
132
+ for (cols = 0; isValid(colDefs[cols]); cols++) ;
133
+ if (check(context, "OCIAttrSet(OCI_ATTR_NUM_COLS)", OCIAttrSet(context->dp, OCI_HTYPE_DIRPATH_CTX, &cols, sizeof(ub2), OCI_ATTR_NUM_COLS, context->err))) {
134
+ return OCI_ERROR;
135
+ }
136
+
137
+ OCIParam *columns;
138
+ if (check(context, "OCIAttrGet(OCI_ATTR_LIST_COLUMNS)", OCIAttrGet(context->dp, OCI_HTYPE_DIRPATH_CTX, &columns, (ub4*)0, OCI_ATTR_LIST_COLUMNS, context->err))) {
139
+ return OCI_ERROR;
140
+ }
141
+
142
+ for (int i = 0; i < cols; i++) {
143
+ EMBULK_OUTPUT_ORACLE_OCI_COL_DEF &colDef = colDefs[i];
144
+ OCIParam *column;
145
+ if (check(context, "OCIParamGet(OCI_DTYPE_PARAM)", OCIParamGet(columns, OCI_DTYPE_PARAM, context->err, (void**)&column, i + 1))) {
146
+ return OCI_ERROR;
147
+ }
148
+ if (check(context, "OCIAttrSet(OCI_ATTR_NAME)", OCIAttrSet(column, OCI_DTYPE_PARAM, (void*)colDef.name, (ub4)strlen(colDef.name), OCI_ATTR_NAME, context->err))) {
149
+ return OCI_ERROR;
150
+ }
151
+ if (check(context, "OCIAttrSet(OCI_ATTR_DATA_TYPE)", OCIAttrSet(column, OCI_DTYPE_PARAM, &colDef.type, sizeof(ub4), OCI_ATTR_DATA_TYPE, context->err))) {
152
+ return OCI_ERROR;
153
+ }
154
+ if (check(context, "OCIAttrSet(OCI_ATTR_DATA_SIZE)", OCIAttrSet(column, OCI_DTYPE_PARAM, &colDef.size, sizeof(ub4), OCI_ATTR_DATA_SIZE, context->err))) {
155
+ return OCI_ERROR;
156
+ }
157
+ /*
158
+ if (check(context, "OCIAttrSet(OCI_ATTR_PRECISION)", OCIAttrSet(column, OCI_DTYPE_PARAM, &colDefs[i].precision, sizeof(ub4), OCI_ATTR_PRECISION, context->err))) {
159
+ return OCI_ERROR;
160
+ }
161
+ if (check(context, "OCIAttrSet(OCI_ATTR_SCALE)", OCIAttrSet(column, OCI_DTYPE_PARAM, &colDefs[i].scale, sizeof(ub4), OCI_ATTR_SCALE, context->err))) {
162
+ return OCI_ERROR;
163
+ }
164
+ */
165
+ if (colDef.dateFormat != NULL) {
166
+ if (check(context, "OCIAttrSet(OCI_ATTR_DATEFORMAT)", OCIAttrSet(column, OCI_DTYPE_PARAM, (void*)colDef.dateFormat, (ub4)strlen(colDef.dateFormat), OCI_ATTR_DATEFORMAT, context->err))) {
167
+ return OCI_ERROR;
168
+ }
169
+ }
170
+
171
+ if (check(context, "OCIDescriptorFree(OCI_DTYPE_PARAM)", OCIDescriptorFree(column, OCI_DTYPE_PARAM))) {
172
+ return OCI_ERROR;
173
+ }
174
+ }
175
+
176
+ if (check(context, "OCIDirPathPrepare", OCIDirPathPrepare(context->dp, context->svc, context->err))) {
177
+ return OCI_ERROR;
178
+ }
179
+
180
+ // direct path column array
181
+ if (check(context, "OCIHandleAlloc(OCI_HTYPE_DIRPATH_COLUMN_ARRAY)", OCIHandleAlloc(
182
+ context->dp,
183
+ (void **)&context->dpca,
184
+ OCI_HTYPE_DIRPATH_COLUMN_ARRAY,
185
+ (size_t)0,
186
+ (void **)0))) {
187
+ return OCI_ERROR;
188
+ }
189
+
190
+ // direct path stream
191
+ if (check(context, "OCIHandleAlloc(OCI_HTYPE_DIRPATH_STREAM)", OCIHandleAlloc(
192
+ context->dp,
193
+ (void **)&context->dpstr,
194
+ OCI_HTYPE_DIRPATH_STREAM,
195
+ (size_t)0,
196
+ (void **)0))) {
197
+ return OCI_ERROR;
198
+ }
199
+
200
+ return OCI_SUCCESS;
201
+ }
202
+
203
+ static void intToSqlInt(int n, char* buffer)
204
+ {
205
+ buffer[0] = n & 0xFF;
206
+ buffer[1] = (n >> 8) & 0xFF;
207
+ buffer[2] = (n >> 16) & 0xFF;
208
+ buffer[3] = (n >> 24) & 0xFF;
209
+ }
210
+
211
+ static int strToSqlInt(const char *s, int size, char* buffer)
212
+ {
213
+ int n = 0;
214
+ for (int i = 0; i < size; i++) {
215
+ if (s[i] < '0' || s[i] > '9') {
216
+ return OCI_ERROR;
217
+ }
218
+ n = n * 10 + s[i] - '0';
219
+ }
220
+
221
+ intToSqlInt(n, buffer);
222
+
223
+ return OCI_SUCCESS;
224
+ }
225
+
226
+ static int loadRows(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, ub4 rowCount)
227
+ {
228
+ for (ub4 offset = 0; offset < rowCount;) {
229
+ if (check(context, "OCIDirPathStreamReset", OCIDirPathStreamReset(context->dpstr, context->err))) {
230
+ return OCI_ERROR;
231
+ }
232
+
233
+ sword result = OCIDirPathColArrayToStream(context->dpca, context->dp, context->dpstr, context->err, rowCount, offset);
234
+ if (result != OCI_SUCCESS && result != OCI_CONTINUE) {
235
+ check(context, "OCIDirPathColArrayToStream", result);
236
+ return OCI_ERROR;
237
+ }
238
+
239
+ if (check(context, "OCIDirPathLoadStream", OCIDirPathLoadStream(context->dp, context->dpstr, context->err))) {
240
+ return OCI_ERROR;
241
+ }
242
+
243
+ if (result == OCI_SUCCESS) {
244
+ offset = rowCount;
245
+ } else {
246
+ ub4 temp;
247
+ if (check(context, "OCIAttrGet(OCI_ATTR_ROW_COUNT)", OCIAttrGet(context->dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY, &temp, 0, OCI_ATTR_ROW_COUNT, context->err))) {
248
+ return OCI_ERROR;
249
+ }
250
+ offset += temp;
251
+ }
252
+ }
253
+
254
+ return OCI_SUCCESS;
255
+ }
256
+
257
+
258
+ int embulk_output_oracle_loadBuffer(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, EMBULK_OUTPUT_ORACLE_OCI_COL_DEF *colDefs, const char *buffer, int rowCount)
259
+ {
260
+ ub4 maxRowCount = 0;
261
+ if (check(context, "OCIAttrGet(OCI_ATTR_NUM_ROWS)", OCIAttrGet(context->dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY, &maxRowCount, 0, OCI_ATTR_NUM_ROWS, context->err))) {
262
+ return OCI_ERROR;
263
+ }
264
+
265
+ int rowSize = 0;
266
+ for (int col = 0; isValid(colDefs[col]); col++) {
267
+ rowSize += colDefs[col].size;
268
+ }
269
+ const char *current = buffer;
270
+
271
+ int colArrayRowCount = 0;
272
+ for (int row = 0; row < rowCount; row++) {
273
+ for (int col = 0; isValid(colDefs[col]); col++) {
274
+ ub4 size = colDefs[col].size;
275
+ if (colDefs[col].type == SQLT_CHR) {
276
+ size = (ub4)strnlen(current, size);
277
+ }
278
+
279
+ if (check(context, "OCIDirPathColArrayEntrySet", OCIDirPathColArrayEntrySet(context->dpca, context->err, colArrayRowCount, col, (ub1*)current, size, OCI_DIRPATH_COL_COMPLETE))) {
280
+ return OCI_ERROR;
281
+ }
282
+ current += colDefs[col].size;
283
+ }
284
+
285
+ colArrayRowCount++;
286
+ if (colArrayRowCount == maxRowCount) {
287
+ if (loadRows(context, colArrayRowCount)) {
288
+ return OCI_ERROR;
289
+ }
290
+
291
+ colArrayRowCount = 0;
292
+ }
293
+ }
294
+
295
+ if (colArrayRowCount > 0) {
296
+ if (loadRows(context, colArrayRowCount)) {
297
+ return OCI_ERROR;
298
+ }
299
+ }
300
+
301
+ return OCI_SUCCESS;
302
+ }
303
+
304
+
305
+ int embulk_output_oracle_loadCSV(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context, EMBULK_OUTPUT_ORACLE_OCI_COL_DEF *colDefs, const char *csvFileName)
306
+ {
307
+ printf("load csv file \"%s\".\r\n", csvFileName);
308
+ if ((context->csv = fopen(csvFileName, "r")) == NULL) {
309
+ printf("Cannot open file.");
310
+ return OCI_ERROR;
311
+ }
312
+
313
+ ub4 maxRowCount = 0;
314
+ if (check(context, "OCIAttrGet(OCI_ATTR_NUM_ROWS)", OCIAttrGet(context->dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY, &maxRowCount, 0, OCI_ATTR_NUM_ROWS, context->err))) {
315
+ return OCI_ERROR;
316
+ }
317
+
318
+ int rowSize = 0;
319
+ for (int i = 0; isValid(colDefs[i]); i++) {
320
+ rowSize += colDefs[i].size;
321
+ }
322
+
323
+ // + 1 for '\0'
324
+ if ((context->buffer = (char*)malloc(rowSize * maxRowCount + 1)) == NULL) {
325
+ printf("Cannot alloc memory.");
326
+ return OCI_ERROR;
327
+ }
328
+ char *current = context->buffer;
329
+
330
+ // TODO: support a line over 1,000 bytes
331
+ char line[1000];
332
+ int row = 0;
333
+ while (fgets(line, sizeof(line), context->csv) != NULL) {
334
+ size_t len = strlen(line);
335
+ int col = 0;
336
+ for (const char *p = line; p < line + len;) {
337
+ const char *comma = strchr(p, ',');
338
+ const char *next;
339
+ ub4 size;
340
+ if (comma != NULL) {
341
+ size = (ub4)(comma - p);
342
+ next = comma + 1;
343
+ } else {
344
+ size = (ub4)(line + len - p);
345
+ if (size > 0 && p[size - 1] == '\n') size--;
346
+ if (size > 0 && p[size - 1] == '\r') size--;
347
+ next = line + len;
348
+ }
349
+
350
+ if (colDefs[col].type == SQLT_INT) {
351
+ if (strToSqlInt(p, size, current)) {
352
+ printf("Not a number : \"%s\"\r\n", p);
353
+ return OCI_ERROR;
354
+ }
355
+ size = colDefs[col].size;
356
+ } else if (colDefs[col].type == SQLT_CHR) {
357
+ strncpy(current, p, size);
358
+ } else {
359
+ printf("Unsupported type : %d\r\n", colDefs[col].type);
360
+ return OCI_ERROR;
361
+ }
362
+
363
+ if (check(context, "OCIDirPathColArrayEntrySet", OCIDirPathColArrayEntrySet(context->dpca, context->err, row, col, (ub1*)current, size, OCI_DIRPATH_COL_COMPLETE))) {
364
+ return OCI_ERROR;
365
+ }
366
+
367
+ p = next;
368
+ current += size;
369
+ col++;
370
+ }
371
+
372
+ row++;
373
+ if (row == maxRowCount) {
374
+ printf("Load %d rows.\r\n", row);
375
+ if (loadRows(context, row)) {
376
+ return OCI_ERROR;
377
+ }
378
+
379
+ current = context->buffer;
380
+ row = 0;
381
+ }
382
+ }
383
+
384
+ if (row > 0) {
385
+ printf("Load %d rows.\r\n", row);
386
+ if (loadRows(context, row)) {
387
+ return OCI_ERROR;
388
+ }
389
+ }
390
+
391
+ free(context->buffer);
392
+ context->buffer = NULL;
393
+
394
+ fclose(context->csv);
395
+ context->csv = NULL;
396
+
397
+ return OCI_SUCCESS;
398
+ }
399
+
400
+ int embulk_output_oracle_commitDirPath(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context)
401
+ {
402
+ if (check(context, "OCIDirPathFinish", OCIDirPathFinish(context->dp, context->err))) {
403
+ return OCI_ERROR;
404
+ }
405
+
406
+ if (check(context, "OCILogoff", OCILogoff(context->svc, context->err))) {
407
+ return OCI_ERROR;
408
+ }
409
+
410
+ return OCI_SUCCESS;
411
+ }
412
+
413
+ int embulk_output_oracle_rollbackDirPath(EMBULK_OUTPUT_ORACLE_OCI_CONTEXT *context)
414
+ {
415
+ if (check(context, "OCIDirPathAbort", OCIDirPathAbort(context->dp, context->err))) {
416
+ return OCI_ERROR;
417
+ }
418
+
419
+ if (check(context, "OCILogoff", OCILogoff(context->svc, context->err))) {
420
+ return OCI_ERROR;
421
+ }
422
+
423
+ return OCI_SUCCESS;
424
+ }