embulk-output-oracle 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +160 -160
  3. data/build.gradle +6 -6
  4. data/classpath/{embulk-output-jdbc-0.4.0.jar → embulk-output-jdbc-0.4.1.jar} +0 -0
  5. data/classpath/{embulk-output-oracle-0.4.0.jar → embulk-output-oracle-0.4.1.jar} +0 -0
  6. data/lib/embulk/output/oracle.rb +3 -3
  7. data/src/main/cpp/common/dir-path-load.cpp +424 -424
  8. data/src/main/cpp/common/dir-path-load.h +36 -36
  9. data/src/main/cpp/common/embulk-output-oracle.cpp +196 -196
  10. data/src/main/cpp/common/org_embulk_output_oracle_oci_OCI.h +77 -77
  11. data/src/main/cpp/linux/build.sh +21 -21
  12. data/src/main/cpp/win/build.bat +31 -31
  13. data/src/main/cpp/win/dllmain.cpp +25 -25
  14. data/src/main/cpp/win/embulk-output-oracle.sln +39 -39
  15. data/src/main/cpp/win/embulk-output-oracle.vcxproj +175 -175
  16. data/src/main/java/org/embulk/output/OracleOutputPlugin.java +153 -153
  17. data/src/main/java/org/embulk/output/oracle/DirectBatchInsert.java +290 -290
  18. data/src/main/java/org/embulk/output/oracle/InsertMethod.java +8 -8
  19. data/src/main/java/org/embulk/output/oracle/OracleCharset.java +32 -32
  20. data/src/main/java/org/embulk/output/oracle/OracleOutputConnection.java +165 -165
  21. data/src/main/java/org/embulk/output/oracle/OracleOutputConnector.java +49 -49
  22. data/src/main/java/org/embulk/output/oracle/TimestampFormat.java +37 -37
  23. data/src/main/java/org/embulk/output/oracle/oci/ColumnDefinition.java +26 -26
  24. data/src/main/java/org/embulk/output/oracle/oci/OCI.java +139 -139
  25. data/src/main/java/org/embulk/output/oracle/oci/OCIManager.java +64 -64
  26. data/src/main/java/org/embulk/output/oracle/oci/OCIWrapper.java +96 -96
  27. data/src/main/java/org/embulk/output/oracle/oci/RowBuffer.java +105 -105
  28. data/src/main/java/org/embulk/output/oracle/oci/TableDefinition.java +24 -24
  29. data/src/test/cpp/common/embulk-output-oracle-test.cpp +69 -69
  30. data/src/test/cpp/linux/build.sh +19 -19
  31. data/src/test/cpp/win/build.bat +28 -28
  32. data/src/test/cpp/win/embulk-output-oracle-test.vcxproj +154 -154
  33. data/src/test/java/org/embulk/input/filesplit/LocalFileSplitInputPlugin.java +187 -187
  34. data/src/test/java/org/embulk/input/filesplit/PartialFile.java +49 -49
  35. data/src/test/java/org/embulk/input/filesplit/PartialFileInputStream.java +154 -154
  36. data/src/test/java/org/embulk/output/oracle/ChildFirstClassLoader.java +42 -42
  37. data/src/test/java/org/embulk/output/oracle/EmbulkPluginTester.java +124 -120
  38. data/src/test/java/org/embulk/output/oracle/EmptyConfigSource.java +105 -100
  39. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java +38 -14
  40. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTestImpl.java +540 -484
  41. data/src/test/java/org/embulk/output/oracle/TimestampFormatTest.java +57 -57
  42. data/src/test/resources/data/test1/test1.csv +3 -3
  43. data/src/test/resources/yml/test-insert-direct-direct-method.yml +26 -0
  44. data/src/test/resources/yml/test-insert-direct-empty.yml +25 -0
  45. data/src/test/resources/yml/{test-insert-oci-split.yml → test-insert-direct-oci-method-split.yml} +28 -28
  46. data/src/test/resources/yml/{test-insert-oci.yml → test-insert-direct-oci-method.yml} +28 -28
  47. data/src/test/resources/yml/test-insert-direct.yml +25 -26
  48. data/src/test/resources/yml/test-insert-empty.yml +27 -0
  49. data/src/test/resources/yml/test-insert.yml +27 -25
  50. data/src/test/resources/yml/test-replace-empty.yml +31 -0
  51. data/src/test/resources/yml/test-replace-long-name-multibyte.yml +31 -31
  52. data/src/test/resources/yml/test-replace-long-name.yml +31 -31
  53. data/src/test/resources/yml/test-replace.yml +31 -31
  54. data/src/test/resources/yml/test-string-timestamp.yml +28 -28
  55. data/src/test/resources/yml/test-url.yml +24 -24
  56. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 885c9eec0bf737ff77ed157431bbe16f2db54fbf
4
- data.tar.gz: d2044b92a9fcd9148aaf40e3fdb2d2609db6e88e
3
+ metadata.gz: 3b4d93aafce15f375d5257efbb522a5af95077f7
4
+ data.tar.gz: b9b37139ed68870c67dda33257282f8917bd7d65
5
5
  SHA512:
6
- metadata.gz: fa3ca6314c186a4d2d682cc9d6d4db42c68fe4bbb9baae74404c43339d0d222a44a9d62e3a6de56bb1c7a2e18114b048a4cd1f9ba796d88f240a8753f3642c2a
7
- data.tar.gz: 07b1d530ca63c91edc7904a1dd6089b131a684ff9525c44acb127150a3785b212f22d8a19185140e815d2b87bd48961ad215fcac97e6d0cfe2cc53683f9438da
6
+ metadata.gz: 0d47e75da1ca1606838f5549d48a4c45835ade69d1582ef36f1e15ad0d4c33eeb7b91405e9695515de572ebea7b24d7b6d730f55cc61dd6f1264f1596561e92d
7
+ data.tar.gz: 5e730ea7a6a27b14ce0a395b0d370c07c06d09e1d6c05ab827476954c1da62729077bc0217577e1c53c555fa38e1f486d8593059e892ac6fae8758cc5406c718
data/README.md CHANGED
@@ -1,160 +1,160 @@
1
- # Oracle output plugins for Embulk
2
-
3
- Oracle output plugins for Embulk loads records to Oracle.
4
-
5
- ## Overview
6
-
7
- * **Plugin type**: output
8
- * **Load all or nothing**: depnds on the mode. see bellow.
9
- * **Resume supported**: depnds on the mode. see bellow.
10
-
11
- ## Configuration
12
-
13
- - **driver_path**: path to the jar file of the Oracle JDBC driver (string)
14
- - **host**: database host name (string, required if url is not set or insert_method is "oci")
15
- - **port**: database port number (integer, default: 1521)
16
- - **user**: database login user name (string, required)
17
- - **password**: database login password (string, default: "")
18
- - **database**: destination database name (string, required if url is not set or insert_method is "oci")
19
- - **url**: URL of the JDBC connection (string, optional)
20
- - **table**: destination table name (string, required)
21
- - **options**: extra connection properties (hash, default: {})
22
- - **mode**: "insert", "insert_direct", "truncate_insert", or "replace". See bellow. (string, required)
23
- - **insert_method**: see below
24
- - **batch_size**: size of a single batch insert (integer, default: 16777216)
25
- - **default_timezone**: If input column type (embulk type) is timestamp, this plugin needs to format the timestamp into a SQL string. This default_timezone option is used to control the timezone. You can overwrite timezone for each columns using column_options option. (string, default: `UTC`)
26
- - **column_options**: advanced: a key-value pairs where key is a column name and value is options for the column.
27
- - **type**: type of a column when this plugin creates new tables (e.g. `VARCHAR(255)`, `INTEGER NOT NULL UNIQUE`). This used when this plugin creates intermediate tables (insert, truncate_insert and merge modes), when it creates the target table (insert_direct and replace modes), and when it creates nonexistent target table automatically. (string, default: depends on input column type. `BIGINT` if input column type is long, `BOOLEAN` if boolean, `DOUBLE PRECISION` if double, `CLOB` if string, `TIMESTAMP` if timestamp)
28
- - **value_type**: This plugin converts input column type (embulk type) into a database type to build a INSERT statement. This value_type option controls the type of the value in a INSERT statement. (string, default: depends on input column type. Available values options are: `byte`, `short`, `int`, `long`, `double`, `float`, `boolean`, `string`, `nstring`, `date`, `time`, `timestamp`, `decimal`, `null`, `pass`)
29
- - **timestamp_format**: If input column type (embulk type) is timestamp and value_type is `string` or `nstring`, this plugin needs to format the timestamp value into a string. This timestamp_format option is used to control the format of the timestamp. (string, default: `%Y-%m-%d %H:%M:%S.%6N`)
30
- - **timezone**: If input column type (embulk type) is timestamp, this plugin needs to format the timestamp value into a SQL string. In this cases, this timezone option is used to control the timezone. (string, value of default_timezone option is used by default)
31
-
32
- ### Modes
33
-
34
- * **insert**:
35
- * Behavior: This mode writes rows to some intermediate tables first. If all those tasks run correctly, runs `INSERT INTO <target_table> SELECT * FROM <intermediate_table_1> UNION ALL SELECT * FROM <intermediate_table_2> UNION ALL ...` query.
36
- * Transactional: Yes. This mode successfully writes all rows, or fails with writing zero rows.
37
- * Resumable: Yes.
38
- * **insert_direct**:
39
- * Behavior: This mode inserts rows to the target table directly.
40
- * Transactional: No. If fails, the target table could have some rows inserted.
41
- * Resumable: No.
42
- * **truncate_insert**:
43
- * Behavior: Same with `insert` mode excepting that it truncates the target table right before the last `INSERT ...` query.
44
- * Transactional: Yes.
45
- * Resumable: Yes.
46
- * **replace**:
47
- * Behavior: Same with `insert` mode excepting that it truncates the target table right before the last `INSERT ...` query.
48
- * Transactional: Yes.
49
- * Resumable: No.
50
-
51
- ### Insert modes
52
-
53
- insert_method supports three options.
54
-
55
- "normal" means normal insert (default). It requires Oracle JDBC driver.
56
-
57
- "direct" means direct path insert. It is faster than 'normal.
58
- It requires Oracle JDBC driver too, but the version 12 driver doesn't work (the version 11 driver works).
59
-
60
- "oci" means direct path insert using OCI(Oracle Call Interface). It is fastest.
61
- It requires both Oracle JDBC driver and Oracle Instant Client (version 12.1.0.2.0).
62
- You must set the library loading path to the OCI library.
63
-
64
- If you use "oci", platform dependent library written in cpp is required.
65
- Windows(x64) library and Linux(x64) are bundled, but others are not bundled.
66
- You should build by yourself and set the library loading path to it.
67
-
68
- ### Example
69
-
70
- ```yaml
71
- out:
72
- type: oracle
73
- driver_path: /opt/oracle/ojdbc6.jar
74
- host: localhost
75
- user: root
76
- password: ""
77
- database: my_database
78
- table: my_table
79
- mode: insert
80
- insert_method: direct
81
- ```
82
-
83
- Advanced configuration:
84
-
85
- ```yaml
86
- out:
87
- type: oracle
88
- driver_path: /opt/oracle/ojdbc6.jar
89
- host: localhost
90
- user: root
91
- password: ""
92
- database: my_database
93
- table: my_table
94
- options: {LoginTimeout: 20000}
95
- mode: insert_direct
96
- insert_method: direct
97
- column_options:
98
- my_col_1: {type: 'VARCHAR(255)'}
99
- my_col_3: {type: 'INT NOT NULL'}
100
- my_col_4: {value_type: string, timestamp_format: `%Y-%m-%d %H:%M:%S %z`, timezone: '-0700'}
101
- my_col_5: {type: 'DECIMAL(18,9)', value_type: pass}
102
- ```
103
-
104
- ### Build
105
-
106
- ```
107
- $ ./gradlew gem
108
- ```
109
-
110
- #### Build environment for native library
111
-
112
- For Windows (x64)
113
-
114
- (1) Install JDK.
115
-
116
- (2) Install Microsoft Visual Studio (only 2010 is tested).
117
-
118
- (3) Install Oracle Instant Client SDK 11.1.0.6.0 for Microsoft Windows (x64).
119
-
120
- (4) Set environment variables.
121
-
122
- * JAVA_HOME
123
- * OCI\_SDK_PATH ("sdk" directory of Oracle Instant Client)
124
-
125
- (5) Open src/main/cpp/win/embulk-output-oracle.sln by Visual Studio and build.
126
-
127
- For Windows command line, the following are needed in addition to (1) - (4).
128
-
129
- (6) Set environment variables.
130
-
131
- * MSVC_PATH (ex. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC)
132
- * MSSDK_PATH (ex. C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A)
133
-
134
- (7) Execute src/main/cpp/win/build.bat .
135
-
136
-
137
- For Linux (x64) (only Ubuntu Server 14.04 is tested)
138
-
139
- (1) Install JDK.
140
-
141
- (2) Install gcc and g++ .
142
-
143
- (3) Install Oracle Instant Client Basic and SDK 11.1.0.6.0 for Linux (x64).
144
-
145
- (4) Create symbolic links of OCI libraries.
146
-
147
- ln -s libocci.so.11.1 libocci.so
148
- ln -s libclntsh.so.11.1 libclntsh.so
149
-
150
- (5) Set environment variables.
151
-
152
- * JAVA_HOME
153
- * OCI_PATH (the directory of Oracle Instant Client Basic and the parent of the "sdk" directory)
154
-
155
- (6) Execute src/main/cpp/linux/build.sh .
156
-
157
- ***
158
- <img src="https://www.yourkit.com/images/yklogo.png" alt="YourKit"/> is used to improve performance of embulk-output-oracle.
159
- YourKit supports open source projects with its full-featured Java Profiler.
160
- YourKit, LLC is the creator of <a href="https://www.yourkit.com/java/profiler/index.jsp">YourKit Java Profiler</a> and <a href="https://www.yourkit.com/.net/profiler/index.jsp">YourKit .NET Profiler</a>, innovative and intelligent tools for profiling Java and .NET applications.
1
+ # Oracle output plugins for Embulk
2
+
3
+ Oracle output plugins for Embulk loads records to Oracle.
4
+
5
+ ## Overview
6
+
7
+ * **Plugin type**: output
8
+ * **Load all or nothing**: depnds on the mode. see bellow.
9
+ * **Resume supported**: depnds on the mode. see bellow.
10
+
11
+ ## Configuration
12
+
13
+ - **driver_path**: path to the jar file of the Oracle JDBC driver (string)
14
+ - **host**: database host name (string, required if url is not set or insert_method is "oci")
15
+ - **port**: database port number (integer, default: 1521)
16
+ - **user**: database login user name (string, required)
17
+ - **password**: database login password (string, default: "")
18
+ - **database**: destination database name (string, required if url is not set or insert_method is "oci")
19
+ - **url**: URL of the JDBC connection (string, optional)
20
+ - **table**: destination table name (string, required)
21
+ - **options**: extra connection properties (hash, default: {})
22
+ - **mode**: "insert", "insert_direct", "truncate_insert", or "replace". See bellow. (string, required)
23
+ - **insert_method**: see below
24
+ - **batch_size**: size of a single batch insert (integer, default: 16777216)
25
+ - **default_timezone**: If input column type (embulk type) is timestamp, this plugin needs to format the timestamp into a SQL string. This default_timezone option is used to control the timezone. You can overwrite timezone for each columns using column_options option. (string, default: `UTC`)
26
+ - **column_options**: advanced: a key-value pairs where key is a column name and value is options for the column.
27
+ - **type**: type of a column when this plugin creates new tables (e.g. `VARCHAR(255)`, `INTEGER NOT NULL UNIQUE`). This used when this plugin creates intermediate tables (insert, truncate_insert and merge modes), when it creates the target table (insert_direct and replace modes), and when it creates nonexistent target table automatically. (string, default: depends on input column type. `BIGINT` if input column type is long, `BOOLEAN` if boolean, `DOUBLE PRECISION` if double, `CLOB` if string, `TIMESTAMP` if timestamp)
28
+ - **value_type**: This plugin converts input column type (embulk type) into a database type to build a INSERT statement. This value_type option controls the type of the value in a INSERT statement. (string, default: depends on input column type. Available values options are: `byte`, `short`, `int`, `long`, `double`, `float`, `boolean`, `string`, `nstring`, `date`, `time`, `timestamp`, `decimal`, `null`, `pass`)
29
+ - **timestamp_format**: If input column type (embulk type) is timestamp and value_type is `string` or `nstring`, this plugin needs to format the timestamp value into a string. This timestamp_format option is used to control the format of the timestamp. (string, default: `%Y-%m-%d %H:%M:%S.%6N`)
30
+ - **timezone**: If input column type (embulk type) is timestamp, this plugin needs to format the timestamp value into a SQL string. In this cases, this timezone option is used to control the timezone. (string, value of default_timezone option is used by default)
31
+
32
+ ### Modes
33
+
34
+ * **insert**:
35
+ * Behavior: This mode writes rows to some intermediate tables first. If all those tasks run correctly, runs `INSERT INTO <target_table> SELECT * FROM <intermediate_table_1> UNION ALL SELECT * FROM <intermediate_table_2> UNION ALL ...` query.
36
+ * Transactional: Yes. This mode successfully writes all rows, or fails with writing zero rows.
37
+ * Resumable: Yes.
38
+ * **insert_direct**:
39
+ * Behavior: This mode inserts rows to the target table directly.
40
+ * Transactional: No. If fails, the target table could have some rows inserted.
41
+ * Resumable: No.
42
+ * **truncate_insert**:
43
+ * Behavior: Same with `insert` mode excepting that it truncates the target table right before the last `INSERT ...` query.
44
+ * Transactional: Yes.
45
+ * Resumable: Yes.
46
+ * **replace**:
47
+ * Behavior: Same with `insert` mode excepting that it truncates the target table right before the last `INSERT ...` query.
48
+ * Transactional: Yes.
49
+ * Resumable: No.
50
+
51
+ ### Insert modes
52
+
53
+ insert_method supports three options.
54
+
55
+ "normal" means normal insert (default). It requires Oracle JDBC driver.
56
+
57
+ "direct" means direct path insert. It is faster than 'normal.
58
+ It requires Oracle JDBC driver too, but the version 12 driver doesn't work (the version 11 driver works).
59
+
60
+ "oci" means direct path insert using OCI(Oracle Call Interface). It is fastest.
61
+ It requires both Oracle JDBC driver and Oracle Instant Client (version 12.1.0.2.0).
62
+ You must set the library loading path to the OCI library.
63
+
64
+ If you use "oci", platform dependent library written in cpp is required.
65
+ Windows(x64) library and Linux(x64) are bundled, but others are not bundled.
66
+ You should build by yourself and set the library loading path to it.
67
+
68
+ ### Example
69
+
70
+ ```yaml
71
+ out:
72
+ type: oracle
73
+ driver_path: /opt/oracle/ojdbc6.jar
74
+ host: localhost
75
+ user: root
76
+ password: ""
77
+ database: my_database
78
+ table: my_table
79
+ mode: insert
80
+ insert_method: direct
81
+ ```
82
+
83
+ Advanced configuration:
84
+
85
+ ```yaml
86
+ out:
87
+ type: oracle
88
+ driver_path: /opt/oracle/ojdbc6.jar
89
+ host: localhost
90
+ user: root
91
+ password: ""
92
+ database: my_database
93
+ table: my_table
94
+ options: {LoginTimeout: 20000}
95
+ mode: insert_direct
96
+ insert_method: direct
97
+ column_options:
98
+ my_col_1: {type: 'VARCHAR(255)'}
99
+ my_col_3: {type: 'INT NOT NULL'}
100
+ my_col_4: {value_type: string, timestamp_format: `%Y-%m-%d %H:%M:%S %z`, timezone: '-0700'}
101
+ my_col_5: {type: 'DECIMAL(18,9)', value_type: pass}
102
+ ```
103
+
104
+ ### Build
105
+
106
+ ```
107
+ $ ./gradlew gem
108
+ ```
109
+
110
+ #### Build environment for native library
111
+
112
+ For Windows (x64)
113
+
114
+ (1) Install JDK.
115
+
116
+ (2) Install Microsoft Visual Studio (only 2010 is tested).
117
+
118
+ (3) Install Oracle Instant Client SDK 11.1.0.6.0 for Microsoft Windows (x64).
119
+
120
+ (4) Set environment variables.
121
+
122
+ * JAVA_HOME
123
+ * OCI\_SDK_PATH ("sdk" directory of Oracle Instant Client)
124
+
125
+ (5) Open src/main/cpp/win/embulk-output-oracle.sln by Visual Studio and build.
126
+
127
+ For Windows command line, the following are needed in addition to (1) - (4).
128
+
129
+ (6) Set environment variables.
130
+
131
+ * MSVC_PATH (ex. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC)
132
+ * MSSDK_PATH (ex. C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A)
133
+
134
+ (7) Execute src/main/cpp/win/build.bat .
135
+
136
+
137
+ For Linux (x64) (only Ubuntu Server 14.04 is tested)
138
+
139
+ (1) Install JDK.
140
+
141
+ (2) Install gcc and g++ .
142
+
143
+ (3) Install Oracle Instant Client Basic and SDK 11.1.0.6.0 for Linux (x64).
144
+
145
+ (4) Create symbolic links of OCI libraries.
146
+
147
+ ln -s libocci.so.11.1 libocci.so
148
+ ln -s libclntsh.so.11.1 libclntsh.so
149
+
150
+ (5) Set environment variables.
151
+
152
+ * JAVA_HOME
153
+ * OCI_PATH (the directory of Oracle Instant Client Basic and the parent of the "sdk" directory)
154
+
155
+ (6) Execute src/main/cpp/linux/build.sh .
156
+
157
+ ***
158
+ <img src="https://www.yourkit.com/images/yklogo.png" alt="YourKit"/> is used to improve performance of embulk-output-oracle.
159
+ YourKit supports open source projects with its full-featured Java Profiler.
160
+ YourKit, LLC is the creator of <a href="https://www.yourkit.com/java/profiler/index.jsp">YourKit Java Profiler</a> and <a href="https://www.yourkit.com/.net/profiler/index.jsp">YourKit .NET Profiler</a>, innovative and intelligent tools for profiling Java and .NET applications.
data/build.gradle CHANGED
@@ -1,6 +1,6 @@
1
- [compileTestJava]*.options*.encoding = 'UTF-8'
2
-
3
- dependencies {
4
- compile project(':embulk-output-jdbc')
5
- testCompile 'org.embulk:embulk-standards:0.5.5'
6
- }
1
+ [compileTestJava]*.options*.encoding = 'UTF-8'
2
+
3
+ dependencies {
4
+ compile project(':embulk-output-jdbc')
5
+ testCompile 'org.embulk:embulk-standards:0.6.16'
6
+ }
@@ -1,3 +1,3 @@
1
- Embulk::JavaPlugin.register_output(
2
- :oracle, "org.embulk.output.OracleOutputPlugin",
3
- File.expand_path('../../../../classpath', __FILE__))
1
+ Embulk::JavaPlugin.register_output(
2
+ :oracle, "org.embulk.output.OracleOutputPlugin",
3
+ File.expand_path('../../../../classpath', __FILE__))
@@ -1,424 +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
- }
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
+ }