20
20
import com .risingwave .proto .Data ;
21
21
import io .grpc .Status ;
22
22
import java .sql .*;
23
+ import java .util .ArrayList ;
23
24
import java .util .Iterator ;
25
+ import java .util .List ;
24
26
import java .util .stream .Collectors ;
25
27
import java .util .stream .IntStream ;
26
28
import org .slf4j .Logger ;
@@ -30,10 +32,13 @@ public class JDBCSink extends SinkBase {
30
32
public static final String INSERT_TEMPLATE = "INSERT INTO %s (%s) VALUES (%s)" ;
31
33
private static final String DELETE_TEMPLATE = "DELETE FROM %s WHERE %s" ;
32
34
private static final String UPDATE_TEMPLATE = "UPDATE %s SET %s WHERE %s" ;
35
+ private static final String ERROR_REPORT_TEMPLATE = "Error when exec %s, message %s" ;
33
36
34
37
private final String tableName ;
35
38
private final Connection conn ;
36
39
private final String jdbcUrl ;
40
+ private final List <String > pkColumnNames ;
41
+ public static final String JDBC_COLUMN_NAME_KEY = "COLUMN_NAME" ;
37
42
38
43
private String updateDeleteConditionBuffer ;
39
44
private Object [] updateDeleteValueBuffer ;
@@ -48,16 +53,38 @@ public JDBCSink(String tableName, String jdbcUrl, TableSchema tableSchema) {
48
53
try {
49
54
this .conn = DriverManager .getConnection (jdbcUrl );
50
55
this .conn .setAutoCommit (false );
56
+ this .pkColumnNames = getPkColumnNames (conn , tableName );
51
57
} catch (SQLException e ) {
52
- throw Status .INTERNAL .withCause (e ).asRuntimeException ();
58
+ throw Status .INTERNAL
59
+ .withDescription (
60
+ String .format (ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
61
+ .asRuntimeException ();
62
+ }
63
+ }
64
+
65
+ private static List <String > getPkColumnNames (Connection conn , String tableName ) {
66
+ List <String > pkColumnNames = new ArrayList <>();
67
+ try {
68
+ var pks = conn .getMetaData ().getPrimaryKeys (null , null , tableName );
69
+ while (pks .next ()) {
70
+ pkColumnNames .add (pks .getString (JDBC_COLUMN_NAME_KEY ));
71
+ }
72
+ } catch (SQLException e ) {
73
+ throw Status .INTERNAL
74
+ .withDescription (
75
+ String .format (ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
76
+ .asRuntimeException ();
53
77
}
78
+ LOG .info ("detected pk {}" , pkColumnNames );
79
+ return pkColumnNames ;
54
80
}
55
81
56
82
public JDBCSink (Connection conn , TableSchema tableSchema , String tableName ) {
57
83
super (tableSchema );
58
84
this .tableName = tableName ;
59
85
this .jdbcUrl = null ;
60
86
this .conn = conn ;
87
+ this .pkColumnNames = getPkColumnNames (conn , tableName );
61
88
}
62
89
63
90
private PreparedStatement prepareStatement (SinkRow row ) {
@@ -79,35 +106,75 @@ private PreparedStatement prepareStatement(SinkRow row) {
79
106
}
80
107
return stmt ;
81
108
} catch (SQLException e ) {
82
- throw io .grpc .Status .INTERNAL .withCause (e ).asRuntimeException ();
109
+ throw io .grpc .Status .INTERNAL
110
+ .withDescription (
111
+ String .format (
112
+ ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
113
+ .asRuntimeException ();
83
114
}
84
115
case DELETE :
85
- String deleteCondition =
86
- getTableSchema ().getPrimaryKeys ().stream ()
87
- .map (key -> key + " = ?" )
88
- .collect (Collectors .joining (" AND " ));
116
+ String deleteCondition ;
117
+ if (this .pkColumnNames .isEmpty ()) {
118
+ deleteCondition =
119
+ IntStream .range (0 , getTableSchema ().getNumColumns ())
120
+ .mapToObj (
121
+ index ->
122
+ getTableSchema ().getColumnNames ()[index ]
123
+ + " = ?" )
124
+ .collect (Collectors .joining (" AND " ));
125
+ } else {
126
+ deleteCondition =
127
+ this .pkColumnNames .stream ()
128
+ .map (key -> key + " = ?" )
129
+ .collect (Collectors .joining (" AND " ));
130
+ }
89
131
String deleteStmt = String .format (DELETE_TEMPLATE , tableName , deleteCondition );
90
132
try {
91
133
int placeholderIdx = 1 ;
92
134
PreparedStatement stmt =
93
135
conn .prepareStatement (deleteStmt , Statement .RETURN_GENERATED_KEYS );
94
- for (String primaryKey : getTableSchema (). getPrimaryKeys () ) {
136
+ for (String primaryKey : this . pkColumnNames ) {
95
137
Object fromRow = getTableSchema ().getFromRow (primaryKey , row );
96
138
stmt .setObject (placeholderIdx ++, fromRow );
97
139
}
98
140
return stmt ;
99
141
} catch (SQLException e ) {
100
- throw Status .INTERNAL .withCause (e ).asRuntimeException ();
142
+ throw Status .INTERNAL
143
+ .withDescription (
144
+ String .format (
145
+ ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
146
+ .asRuntimeException ();
101
147
}
102
148
case UPDATE_DELETE :
103
- updateDeleteConditionBuffer =
104
- getTableSchema ().getPrimaryKeys ().stream ()
105
- .map (key -> key + " = ?" )
106
- .collect (Collectors .joining (" AND " ));
107
- updateDeleteValueBuffer =
108
- getTableSchema ().getPrimaryKeys ().stream ()
109
- .map (key -> getTableSchema ().getFromRow (key , row ))
110
- .toArray ();
149
+ if (this .pkColumnNames .isEmpty ()) {
150
+ updateDeleteConditionBuffer =
151
+ IntStream .range (0 , getTableSchema ().getNumColumns ())
152
+ .mapToObj (
153
+ index ->
154
+ getTableSchema ().getColumnNames ()[index ]
155
+ + " = ?" )
156
+ .collect (Collectors .joining (" AND " ));
157
+ updateDeleteValueBuffer =
158
+ IntStream .range (0 , getTableSchema ().getNumColumns ())
159
+ .mapToObj (
160
+ index ->
161
+ getTableSchema ()
162
+ .getFromRow (
163
+ getTableSchema ()
164
+ .getColumnNames ()[
165
+ index ],
166
+ row ))
167
+ .toArray ();
168
+ } else {
169
+ updateDeleteConditionBuffer =
170
+ this .pkColumnNames .stream ()
171
+ .map (key -> key + " = ?" )
172
+ .collect (Collectors .joining (" AND " ));
173
+ updateDeleteValueBuffer =
174
+ this .pkColumnNames .stream ()
175
+ .map (key -> getTableSchema ().getFromRow (key , row ))
176
+ .toArray ();
177
+ }
111
178
LOG .debug (
112
179
"update delete condition: {} on values {}" ,
113
180
updateDeleteConditionBuffer ,
@@ -144,7 +211,11 @@ private PreparedStatement prepareStatement(SinkRow row) {
144
211
updateDeleteValueBuffer = null ;
145
212
return stmt ;
146
213
} catch (SQLException e ) {
147
- throw Status .INTERNAL .withCause (e ).asRuntimeException ();
214
+ throw Status .INTERNAL
215
+ .withDescription (
216
+ String .format (
217
+ ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
218
+ .asRuntimeException ();
148
219
}
149
220
default :
150
221
throw Status .INVALID_ARGUMENT
@@ -163,10 +234,14 @@ public void write(Iterator<SinkRow> rows) {
163
234
}
164
235
if (stmt != null ) {
165
236
try {
166
- LOG .debug ("Executing statement: " + stmt );
237
+ LOG .debug ("Executing statement: {}" , stmt );
167
238
stmt .executeUpdate ();
168
239
} catch (SQLException e ) {
169
- throw Status .INTERNAL .withCause (e ).asRuntimeException ();
240
+ throw Status .INTERNAL
241
+ .withDescription (
242
+ String .format (
243
+ ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
244
+ .asRuntimeException ();
170
245
}
171
246
} else {
172
247
throw Status .INTERNAL
@@ -187,7 +262,10 @@ public void sync() {
187
262
try {
188
263
conn .commit ();
189
264
} catch (SQLException e ) {
190
- throw io .grpc .Status .INTERNAL .withCause (e ).asRuntimeException ();
265
+ throw io .grpc .Status .INTERNAL
266
+ .withDescription (
267
+ String .format (ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
268
+ .asRuntimeException ();
191
269
}
192
270
}
193
271
@@ -196,7 +274,10 @@ public void drop() {
196
274
try {
197
275
conn .close ();
198
276
} catch (SQLException e ) {
199
- throw io .grpc .Status .INTERNAL .withCause (e ).asRuntimeException ();
277
+ throw io .grpc .Status .INTERNAL
278
+ .withDescription (
279
+ String .format (ERROR_REPORT_TEMPLATE , e .getSQLState (), e .getMessage ()))
280
+ .asRuntimeException ();
200
281
}
201
282
}
202
283
0 commit comments