4
4
5
5
package io .airbyte .integrations .destination .mysql ;
6
6
7
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_AB_EXTRACTED_AT ;
8
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_AB_ID ;
9
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_AB_LOADED_AT ;
10
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_AB_META ;
11
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_AB_RAW_ID ;
12
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_DATA ;
13
+ import static io .airbyte .cdk .integrations .base .JavaBaseConstants .COLUMN_NAME_EMITTED_AT ;
14
+
7
15
import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
8
16
import io .airbyte .cdk .db .jdbc .JdbcDatabase ;
9
17
import io .airbyte .cdk .integrations .base .JavaBaseConstants ;
14
22
import java .nio .file .Files ;
15
23
import java .sql .SQLException ;
16
24
import java .sql .Statement ;
25
+ import java .util .Arrays ;
17
26
import java .util .List ;
27
+ import java .util .Spliterator ;
28
+ import java .util .stream .Collectors ;
29
+ import java .util .stream .IntStream ;
30
+ import java .util .stream .StreamSupport ;
18
31
19
32
@ SuppressFBWarnings (
20
33
value = {"SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE" },
@@ -42,7 +55,15 @@ public void insertRecordsInternal(final JdbcDatabase database,
42
55
try {
43
56
final File tmpFile = Files .createTempFile (tmpTableName + "-" , ".tmp" ).toFile ();
44
57
45
- loadDataIntoTable (database , records , schemaName , tmpTableName , tmpFile );
58
+ loadDataIntoTable (
59
+ database ,
60
+ records ,
61
+ schemaName ,
62
+ tmpTableName ,
63
+ tmpFile ,
64
+ COLUMN_NAME_AB_ID ,
65
+ COLUMN_NAME_DATA ,
66
+ COLUMN_NAME_EMITTED_AT );
46
67
47
68
Files .delete (tmpFile .toPath ());
48
69
} catch (final IOException e ) {
@@ -56,25 +77,82 @@ protected void insertRecordsInternalV2(final JdbcDatabase database,
56
77
final String schemaName ,
57
78
final String tableName )
58
79
throws Exception {
59
- throw new UnsupportedOperationException ("mysql does not yet support DV2" );
80
+ if (records .isEmpty ()) {
81
+ return ;
82
+ }
83
+
84
+ verifyLocalFileEnabled (database );
85
+ try {
86
+ final File tmpFile = Files .createTempFile (tableName + "-" , ".tmp" ).toFile ();
87
+
88
+ loadDataIntoTable (
89
+ database ,
90
+ records ,
91
+ schemaName ,
92
+ tableName ,
93
+ tmpFile ,
94
+ COLUMN_NAME_AB_RAW_ID ,
95
+ COLUMN_NAME_DATA ,
96
+ COLUMN_NAME_AB_EXTRACTED_AT ,
97
+ COLUMN_NAME_AB_LOADED_AT ,
98
+ COLUMN_NAME_AB_META );
99
+ Files .delete (tmpFile .toPath ());
100
+ } catch (final IOException e ) {
101
+ throw new SQLException (e );
102
+ }
60
103
}
61
104
62
105
private void loadDataIntoTable (final JdbcDatabase database ,
63
106
final List <PartialAirbyteMessage > records ,
64
107
final String schemaName ,
65
108
final String tmpTableName ,
66
- final File tmpFile )
109
+ final File tmpFile ,
110
+ final String ... columnNames )
67
111
throws SQLException {
68
112
database .execute (connection -> {
69
113
try {
70
114
writeBatchToFile (tmpFile , records );
71
115
72
116
final String absoluteFile = "'" + tmpFile .getAbsolutePath () + "'" ;
73
117
74
- final String query = String .format (
75
- "LOAD DATA LOCAL INFILE %s INTO TABLE %s.%s FIELDS TERMINATED BY ',' ENCLOSED BY '\" ' ESCAPED BY '\\ \" ' LINES TERMINATED BY '\\ r\\ n'" ,
76
- absoluteFile , schemaName , tmpTableName );
118
+ /*
119
+ We want to generate a query like:
120
+
121
+ LOAD DATA LOCAL INFILE '/a/b/c' INTO TABLE foo.bar
122
+ FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\"'
123
+ LINES TERMINATED BY '\r\n'
124
+ (@c0, @c1, @c2, @c3, @c4)
125
+ SET
126
+ _airybte_raw_id = NULLIT(@c0, ''),
127
+ _airbyte_data = NULLIT(@c1, ''),
128
+ _airbyte_extracted_at = NULLIT(@c2, ''),
129
+ _airbyte_loaded_at = NULLIT(@c3, ''),
130
+ _airbyte_meta = NULLIT(@c4, '')
77
131
132
+ This is to avoid weird default values (e.g. 0000-00-00 00:00:00) when the value should be NULL.
133
+ */
134
+
135
+ final String colVarDecls = "("
136
+ + IntStream .range (0 , columnNames .length ).mapToObj (i -> "@c" + i ).collect (Collectors .joining ("," ))
137
+ + ")" ;
138
+ final String colAssignments = IntStream .range (0 , columnNames .length )
139
+ .mapToObj (i -> columnNames [i ] + " = NULLIF(@c" + i + ", '')" )
140
+ .collect (Collectors .joining ("," ));
141
+
142
+ final String query = String .format (
143
+ """
144
+ LOAD DATA LOCAL INFILE %s INTO TABLE %s.%s
145
+ FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\ "'
146
+ LINES TERMINATED BY '\\ r\\ n'
147
+ %s
148
+ SET
149
+ %s
150
+ """ ,
151
+ absoluteFile ,
152
+ schemaName ,
153
+ tmpTableName ,
154
+ colVarDecls ,
155
+ colAssignments );
78
156
try (final Statement stmt = connection .createStatement ()) {
79
157
stmt .execute (query );
80
158
}
@@ -129,7 +207,7 @@ private boolean checkIfLocalFileIsEnabled(final JdbcDatabase database) throws SQ
129
207
}
130
208
131
209
@ Override
132
- public String createTableQuery ( final JdbcDatabase database , final String schemaName , final String tableName ) {
210
+ protected String createTableQueryV1 ( String schemaName , String tableName ) {
133
211
// MySQL requires byte information with VARCHAR. Since we are using uuid as value for the column,
134
212
// 256 is enough
135
213
return String .format (
@@ -141,6 +219,28 @@ public String createTableQuery(final JdbcDatabase database, final String schemaN
141
219
schemaName , tableName , JavaBaseConstants .COLUMN_NAME_AB_ID , JavaBaseConstants .COLUMN_NAME_DATA , JavaBaseConstants .COLUMN_NAME_EMITTED_AT );
142
220
}
143
221
222
+ protected String createTableQueryV2 (String schemaName , String tableName ) {
223
+ // MySQL requires byte information with VARCHAR. Since we are using uuid as value for the column,
224
+ // 256 is enough
225
+ return String .format (
226
+ """
227
+ CREATE TABLE IF NOT EXISTS %s.%s (\s
228
+ %s VARCHAR(256) PRIMARY KEY,
229
+ %s JSON,
230
+ %s TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6),
231
+ %s TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6),
232
+ %s JSON
233
+ );
234
+ """ ,
235
+ schemaName ,
236
+ tableName ,
237
+ JavaBaseConstants .COLUMN_NAME_AB_RAW_ID ,
238
+ JavaBaseConstants .COLUMN_NAME_DATA ,
239
+ JavaBaseConstants .COLUMN_NAME_AB_EXTRACTED_AT ,
240
+ JavaBaseConstants .COLUMN_NAME_AB_LOADED_AT ,
241
+ JavaBaseConstants .COLUMN_NAME_AB_META );
242
+ }
243
+
144
244
public static class VersionCompatibility {
145
245
146
246
private final double version ;
0 commit comments