@@ -184,103 +184,130 @@ public void setNull(int index, int oid) throws SQLException {
184
184
bind (index , NULL_OBJECT , oid , binaryTransfer );
185
185
}
186
186
187
+ private static String quoteAndCast (String text , String type , boolean standardConformingStrings )
188
+ {
189
+ StringBuilder sb = new StringBuilder ((text .length () + 10 ) / 10 * 11 ); // Add 10% for escaping.
190
+ sb .append ("('" );
191
+ try {
192
+ Utils .escapeLiteral (sb , text , standardConformingStrings );
193
+ } catch (SQLException e ) {
194
+ // This should only happen if we have an embedded null
195
+ // and there's not much we can do if we do hit one.
196
+ //
197
+ // To force a server side failure, we deliberately include
198
+ // a zero byte character in the literal to force the server
199
+ // to reject the command.
200
+ sb .append ('\u0000' );
201
+ }
202
+ sb .append ("'" );
203
+ if (type != null ) {
204
+ sb .append ("::" );
205
+ sb .append (type );
206
+ }
207
+ sb .append (")" );
208
+ return sb .toString ();
209
+ }
210
+
187
211
@ Override
188
212
public String toString (int index , boolean standardConformingStrings ) {
189
213
--index ;
190
214
if (paramValues [index ] == null ) {
191
215
return "?" ;
192
216
} else if (paramValues [index ] == NULL_OBJECT ) {
193
217
return "NULL" ;
194
- } else if ((flags [index ] & BINARY ) == BINARY ) {
218
+ }
219
+ String textValue ;
220
+ String type ;
221
+ if ((flags [index ] & BINARY ) == BINARY ) {
195
222
// handle some of the numeric types
196
-
197
223
switch (paramTypes [index ]) {
198
224
case Oid .INT2 :
199
225
short s = ByteConverter .int2 ((byte []) paramValues [index ], 0 );
200
- return Short .toString (s );
226
+ textValue = Short .toString (s );
227
+ type = "int2" ;
228
+ break ;
201
229
202
230
case Oid .INT4 :
203
231
int i = ByteConverter .int4 ((byte []) paramValues [index ], 0 );
204
- return Integer .toString (i );
232
+ textValue = Integer .toString (i );
233
+ type = "int4" ;
234
+ break ;
205
235
206
236
case Oid .INT8 :
207
237
long l = ByteConverter .int8 ((byte []) paramValues [index ], 0 );
208
- return Long .toString (l );
238
+ textValue = Long .toString (l );
239
+ type = "int8" ;
240
+ break ;
209
241
210
242
case Oid .FLOAT4 :
211
243
float f = ByteConverter .float4 ((byte []) paramValues [index ], 0 );
212
244
if (Float .isNaN (f )) {
213
245
return "'NaN'::real" ;
214
246
}
215
- return Float .toString (f );
247
+ textValue = Float .toString (f );
248
+ type = "real" ;
249
+ break ;
216
250
217
251
case Oid .FLOAT8 :
218
252
double d = ByteConverter .float8 ((byte []) paramValues [index ], 0 );
219
253
if (Double .isNaN (d )) {
220
254
return "'NaN'::double precision" ;
221
255
}
222
- return Double .toString (d );
256
+ textValue = Double .toString (d );
257
+ type = "double precision" ;
258
+ break ;
223
259
224
260
case Oid .UUID :
225
- String uuid =
261
+ textValue =
226
262
new UUIDArrayAssistant ().buildElement ((byte []) paramValues [index ], 0 , 16 ).toString ();
227
- return "'" + uuid + "'::uuid" ;
263
+ type = "uuid" ;
264
+ break ;
228
265
229
266
case Oid .POINT :
230
267
RedshiftPoint pgPoint = new RedshiftPoint ();
231
268
pgPoint .setByteValue ((byte []) paramValues [index ], 0 );
232
- return "'" + pgPoint .toString () + "'::point" ;
269
+ textValue = pgPoint .toString ();
270
+ type = "point" ;
271
+ break ;
233
272
234
273
case Oid .BOX :
235
274
RedshiftBox pgBox = new RedshiftBox ();
236
275
pgBox .setByteValue ((byte []) paramValues [index ], 0 );
237
- return "'" + pgBox .toString () + "'::box" ;
276
+ textValue = pgBox .toString ();
277
+ type = "box" ;
278
+ break ;
279
+
280
+ default :
281
+ return "?" ;
238
282
}
239
- return "?" ;
240
283
} else {
241
- String param = paramValues [index ].toString ();
242
-
243
- // add room for quotes + potential escaping.
244
- StringBuilder p = new StringBuilder (3 + (param .length () + 10 ) / 10 * 11 );
245
-
246
- // No E'..' here since escapeLiteral escapes all things and it does not use \123 kind of
247
- // escape codes
248
- p .append ('\'' );
249
- try {
250
- p = Utils .escapeLiteral (p , param , standardConformingStrings );
251
- } catch (SQLException sqle ) {
252
- // This should only happen if we have an embedded null
253
- // and there's not much we can do if we do hit one.
254
- //
255
- // The goal of toString isn't to be sent to the server,
256
- // so we aren't 100% accurate (see StreamWrapper), put
257
- // the unescaped version of the data.
258
- //
259
- p .append (param );
260
- }
261
- p .append ('\'' );
284
+ textValue = paramValues [index ].toString ();
262
285
int paramType = paramTypes [index ];
286
+
263
287
if (paramType == Oid .TIMESTAMP ) {
264
- p . append ( ":: timestamp") ;
288
+ type = " timestamp" ;
265
289
} else if (paramType == Oid .TIMESTAMPTZ ) {
266
- p . append ( ":: timestamp with time zone") ;
290
+ type = " timestamp with time zone" ;
267
291
} else if (paramType == Oid .TIME ) {
268
- p . append ( ":: time") ;
292
+ type = " time" ;
269
293
} else if (paramType == Oid .TIMETZ ) {
270
- p . append ( ":: time with time zone") ;
294
+ type = " time with time zone" ;
271
295
} else if (paramType == Oid .DATE ) {
272
- p . append ( ":: date") ;
296
+ type = " date" ;
273
297
} else if (paramType == Oid .INTERVAL ) {
274
- p . append ( ":: interval") ;
298
+ type = " interval" ;
275
299
} else if (paramType == Oid .INTERVALY2M ) {
276
- p . append ( ":: interval year to month") ;
300
+ type = " interval year to month" ;
277
301
} else if (paramType == Oid .INTERVALD2S ) {
278
- p . append ( ":: interval day to second") ;
302
+ type = " interval day to second" ;
279
303
} else if (paramType == Oid .NUMERIC ) {
280
- p .append ("::numeric" );
304
+ type = "numeric" ;
305
+ }
306
+ else {
307
+ type = null ;
281
308
}
282
- return p .toString ();
283
309
}
310
+ return quoteAndCast (textValue , type , standardConformingStrings );
284
311
}
285
312
286
313
@ Override
0 commit comments