@@ -29,6 +29,7 @@ public class Row {
2929 private FieldMap fieldMap ;
3030 private List <ByteString > values ;
3131 private Query .Row rawRow ;
32+ private boolean lastGetWasNull ;
3233
3334 /**
3435 * Construct a Row from {@link com.youtube.vitess.proto.Query.Row}
@@ -101,96 +102,223 @@ public Object getObject(int columnIndex) throws SQLException {
101102 if (columnIndex >= values .size ()) {
102103 throw new SQLDataException ("invalid columnIndex: " + columnIndex );
103104 }
104- return convertFieldValue (fieldMap .get (columnIndex ), values .get (columnIndex ));
105+ Object value = convertFieldValue (fieldMap .get (columnIndex ), values .get (columnIndex ));
106+ lastGetWasNull = (value == null );
107+ return value ;
105108 }
106109
110+ /**
111+ * Returns the column value, or 0 if the value is SQL NULL.
112+ *
113+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
114+ * or {@link #getObject(String,Class)}.
115+ */
107116 public int getInt (String columnLabel ) throws SQLException {
108117 return getInt (findColumn (columnLabel ));
109118 }
110119
120+ /**
121+ * Returns the column value, or 0 if the value is SQL NULL.
122+ *
123+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
124+ * or {@link #getObject(int,Class)}.
125+ */
111126 public int getInt (int columnIndex ) throws SQLException {
112- return (Integer ) getAndCheckType (columnIndex , Integer .class );
127+ Integer value = getObject (columnIndex , Integer .class );
128+ return value == null ? 0 : value ;
113129 }
114130
115131 public UnsignedLong getULong (String columnLabel ) throws SQLException {
116132 return getULong (findColumn (columnLabel ));
117133 }
118134
119135 public UnsignedLong getULong (int columnIndex ) throws SQLException {
120- return ( UnsignedLong ) getAndCheckType (columnIndex , UnsignedLong .class );
136+ return getObject (columnIndex , UnsignedLong .class );
121137 }
122138
123139 public String getString (String columnLabel ) throws SQLException {
124140 return getString (findColumn (columnLabel ));
125141 }
126142
127143 public String getString (int columnIndex ) throws SQLException {
128- return ( String ) getAndCheckType (columnIndex , String .class );
144+ return getObject (columnIndex , String .class );
129145 }
130146
147+ /**
148+ * Returns the column value, or 0 if the value is SQL NULL.
149+ *
150+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
151+ * or {@link #getObject(String,Class)}.
152+ */
131153 public long getLong (String columnLabel ) throws SQLException {
132154 return getLong (findColumn (columnLabel ));
133155 }
134156
157+ /**
158+ * Returns the column value, or 0 if the value is SQL NULL.
159+ *
160+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
161+ * or {@link #getObject(int,Class)}.
162+ */
135163 public long getLong (int columnIndex ) throws SQLException {
136- return (Long ) getAndCheckType (columnIndex , Long .class );
164+ Long value = getObject (columnIndex , Long .class );
165+ return value == null ? 0 : value ;
137166 }
138167
168+ /**
169+ * Returns the column value, or 0 if the value is SQL NULL.
170+ *
171+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
172+ * or {@link #getObject(String,Class)}.
173+ */
139174 public double getDouble (String columnLabel ) throws SQLException {
140175 return getDouble (findColumn (columnLabel ));
141176 }
142177
178+ /**
179+ * Returns the column value, or 0 if the value is SQL NULL.
180+ *
181+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
182+ * or {@link #getObject(int,Class)}.
183+ */
143184 public double getDouble (int columnIndex ) throws SQLException {
144- return (Double ) getAndCheckType (columnIndex , Double .class );
185+ Double value = getObject (columnIndex , Double .class );
186+ return value == null ? 0 : value ;
145187 }
146188
189+ /**
190+ * Returns the column value, or 0 if the value is SQL NULL.
191+ *
192+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
193+ * or {@link #getObject(String,Class)}.
194+ */
147195 public float getFloat (String columnLabel ) throws SQLException {
148196 return getFloat (findColumn (columnLabel ));
149197 }
150198
199+ /**
200+ * Returns the column value, or 0 if the value is SQL NULL.
201+ *
202+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
203+ * or {@link #getObject(int,Class)}.
204+ */
151205 public float getFloat (int columnIndex ) throws SQLException {
152- return (Float ) getAndCheckType (columnIndex , Float .class );
206+ Float value = getObject (columnIndex , Float .class );
207+ return value == null ? 0 : value ;
153208 }
154209
155210 public DateTime getDateTime (String columnLabel ) throws SQLException {
156211 return getDateTime (findColumn (columnLabel ));
157212 }
158213
159214 public DateTime getDateTime (int columnIndex ) throws SQLException {
160- return ( DateTime ) getAndCheckType (columnIndex , DateTime .class );
215+ return getObject (columnIndex , DateTime .class );
161216 }
162217
163218 public byte [] getBytes (String columnLabel ) throws SQLException {
164219 return getBytes (findColumn (columnLabel ));
165220 }
166221
167222 public byte [] getBytes (int columnIndex ) throws SQLException {
168- return ( byte []) getAndCheckType (columnIndex , byte [].class );
223+ return getObject (columnIndex , byte [].class );
169224 }
170225
171226 public BigDecimal getBigDecimal (String columnLabel ) throws SQLException {
172227 return getBigDecimal (findColumn (columnLabel ));
173228 }
174229
175230 public BigDecimal getBigDecimal (int columnIndex ) throws SQLException {
176- return ( BigDecimal ) getAndCheckType (columnIndex , BigDecimal .class );
231+ return getObject (columnIndex , BigDecimal .class );
177232 }
178233
234+ /**
235+ * Returns the column value, or 0 if the value is SQL NULL.
236+ *
237+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
238+ * or {@link #getObject(String,Class)}.
239+ */
179240 public short getShort (String columnLabel ) throws SQLException {
180241 return getShort (findColumn (columnLabel ));
181242 }
182243
244+ /**
245+ * Returns the column value, or 0 if the value is SQL NULL.
246+ *
247+ * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
248+ * or {@link #getObject(int,Class)}.
249+ */
183250 public short getShort (int columnIndex ) throws SQLException {
184- return (Short ) getAndCheckType (columnIndex , Short .class );
251+ Short value = getObject (columnIndex , Short .class );
252+ return value == null ? 0 : value ;
185253 }
186254
187- private Object getAndCheckType (int columnIndex , Class <?> cls ) throws SQLException {
255+ /**
256+ * Returns the column value, cast to the specified type.
257+ *
258+ * <p>This can be used as an alternative to getters that return primitive
259+ * types, if you need to distinguish between 0 and SQL NULL. For example:
260+ *
261+ * <blockquote><pre>
262+ * Long value = row.getObject(0, Long.class);
263+ * if (value == null) {
264+ * // The value was SQL NULL, not 0.
265+ * }
266+ * </pre></blockquote>
267+ *
268+ * @throws SQLDataException if the type doesn't match the actual value.
269+ */
270+ @ SuppressWarnings ("unchecked" ) // by runtime check
271+ public <T > T getObject (int columnIndex , Class <T > type ) throws SQLException {
188272 Object o = getObject (columnIndex );
189- if (o != null && !cls .isInstance (o )) {
273+ if (o != null && !type .isInstance (o )) {
190274 throw new SQLDataException (
191- "type mismatch, expected:" + cls .getName () + ", actual: " + o .getClass ().getName ());
275+ "type mismatch, expected:" + type .getName () + ", actual: " + o .getClass ().getName ());
192276 }
193- return o ;
277+ return (T ) o ;
278+ }
279+
280+ /**
281+ * Returns the column value, cast to the specified type.
282+ *
283+ * <p>This can be used as an alternative to getters that return primitive
284+ * types, if you need to distinguish between 0 and SQL NULL. For example:
285+ *
286+ * <blockquote><pre>
287+ * Long value = row.getObject("col0", Long.class);
288+ * if (value == null) {
289+ * // The value was SQL NULL, not 0.
290+ * }
291+ * </pre></blockquote>
292+ *
293+ * @throws SQLDataException if the type doesn't match the actual value.
294+ */
295+ public <T > T getObject (String columnLabel , Class <T > type ) throws SQLException {
296+ return getObject (findColumn (columnLabel ), type );
297+ }
298+
299+ /**
300+ * Reports whether the last column read had a value of SQL NULL.
301+ *
302+ * <p>Getter methods that return primitive types, such as {@link #getLong(int)},
303+ * will return 0 if the value is SQL NULL. To distinguish 0 from SQL NULL,
304+ * you can call {@code wasNull()} immediately after retrieving the value.
305+ *
306+ * <p>Note that this is not thread-safe: the value of {@code wasNull()} is only
307+ * trustworthy if there are no concurrent calls on this {@code Row} between the
308+ * call to {@code get*()} and the call to {@code wasNull()}.
309+ *
310+ * <p>As an alternative to {@code wasNull()}, you can use {@link #getObject(int,Class)}
311+ * (e.g. {@code getObject(0, Long.class)} instead of {@code getLong(0)}) to get a
312+ * wrapped {@code Long} value that will be {@code null} if the column value was SQL NULL.
313+ *
314+ * @throws SQLException
315+ */
316+ public boolean wasNull () throws SQLException {
317+ // Note: lastGetWasNull is currently set only in getObject(int),
318+ // which means this relies on the fact that all other get*() methods
319+ // eventually call into getObject(int). The unit tests help to ensure
320+ // this by checking wasNull() after each get*().
321+ return lastGetWasNull ;
194322 }
195323
196324 private static Object convertFieldValue (Field field , ByteString value ) throws SQLException {
0 commit comments