Permalink
Browse files

fix: strict handling of getBoolean and setObject with postgres accept…

…ed values (#732)

Strict support of accepted values in getBoolean and setObject(BOOLEAN)

Discussion: https://www.postgresql.org/message-id/flat/990BE54A-5803-4B5A-A5C8-BD6DBE75B72C%40me.com
  • Loading branch information...
jorsol authored and vlsi committed Jan 27, 2017
1 parent 55e2cd1 commit 4942f7d1cc812feeeca331878334a3d4058615e4
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.jdbc;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Helper class to handle boolean type of PostgreSQL.
* <p>
* Based on values accepted by the PostgreSQL server:
* https://www.postgresql.org/docs/current/static/datatype-boolean.html
*/
class BooleanTypeUtil {
private static final Logger LOGGER = Logger.getLogger(BooleanTypeUtil.class.getName());
private BooleanTypeUtil() {
}
/**
* Cast an Object value to the corresponding boolean value.
*
* @param in Object to cast into boolean
* @return boolean value corresponding to the cast of the object
* @throws PSQLException PSQLState.CANNOT_COERCE
*/
static boolean castToBoolean(final Object in) throws PSQLException {
LOGGER.log(Level.FINE, "Object to cast: {0}", in);
if (in instanceof Boolean) {
return (Boolean) in;
}
if (in instanceof String) {
return from((String) in);
}
if (in instanceof Character) {
return from((Character) in);
}
if (in instanceof Number) {
return from((Number) in, in.getClass().getName());
}
throw cannotCoerceException(in.getClass().getName());
}
private static boolean from(final String strval) throws PSQLException {
// Leading or trailing whitespace is ignored, and case does not matter.
final String val = strval.trim();
if ("1".equals(val) || "true".equalsIgnoreCase(val)
|| "t".equalsIgnoreCase(val) || "yes".equalsIgnoreCase(val)
|| "y".equalsIgnoreCase(val) || "on".equalsIgnoreCase(val)) {
return true;
}
if ("0".equals(val) || "false".equalsIgnoreCase(val)
|| "f".equalsIgnoreCase(val) || "no".equalsIgnoreCase(val)
|| "n".equalsIgnoreCase(val) || "off".equalsIgnoreCase(val)) {
return false;
}
throw cannotCoerceException("String", val);
}
private static boolean from(final Character charval) throws PSQLException {
if ('1' == charval || 't' == charval || 'T' == charval
|| 'y' == charval || 'Y' == charval) {
return true;
}
if ('0' == charval || 'f' == charval || 'F' == charval
|| 'n' == charval || 'N' == charval) {
return false;
}
throw cannotCoerceException("Character", charval.toString());
}
private static boolean from(final Number numval, final String className) throws PSQLException {
// Handles BigDecimal, Byte, Short, Integer, Long Float, Double
// based on the widening primitive conversions.
double value = numval.doubleValue();
if (value == 1.0d) {
return true;
}
if (value == 0.0d) {
return false;
}
throw cannotCoerceException(className, String.valueOf(numval));
}
private static PSQLException cannotCoerceException(final String fromType) {
LOGGER.log(Level.FINE, "Cannot cast type {0} to boolean", fromType);
return new PSQLException(GT.tr("Cannot cast type {0} to boolean", fromType),
PSQLState.CANNOT_COERCE);
}
private static PSQLException cannotCoerceException(final String fromType, final String value) {
LOGGER.log(Level.FINE, "Cannot cast type {0} to boolean: \"{1}\"", new Object[]{fromType, value});
return new PSQLException(GT.tr("Cannot cast type {0} to boolean: \"{1}\"", fromType, value),
PSQLState.CANNOT_COERCE);
}
}
@@ -577,9 +577,9 @@ private Object buildArray(PgArrayList input, int index, int count) throws SQLExc
if (dims > 1 || useObjects) {
oa[length++] = o == null ? null
: (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toBoolean((String) o));
: (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : BooleanTypeUtil.castToBoolean((String) o));
} else {
pa[length++] = o == null ? false : PgResultSet.toBoolean((String) o);
pa[length++] = o == null ? false : BooleanTypeUtil.castToBoolean((String) o);
}
}
} else if (type == Types.SMALLINT || type == Types.INTEGER) {
@@ -206,9 +206,6 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException {
checkClosed();
int oid;
if (sqlType == Types.BOOLEAN) {
sqlType = Types.BIT;
}
switch (sqlType) {
case Types.SQLXML:
oid = Oid.XML;
@@ -252,6 +249,7 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException {
case Types.TIMESTAMP:
oid = Oid.UNSPECIFIED;
break;
case Types.BOOLEAN:
case Types.BIT:
oid = Oid.BOOL;
break;
@@ -527,10 +525,6 @@ public void setObject(int parameterIndex, Object in, int targetSqlType, int scal
return;
}
if (targetSqlType == Types.BOOLEAN) {
targetSqlType = Types.BIT;
}
if (targetSqlType == Types.OTHER && in instanceof UUID
&& connection.haveMinimumServerVersion(ServerVersion.v8_3)) {
setUuid(parameterIndex, (UUID) in);
@@ -649,8 +643,9 @@ public void setObject(int parameterIndex, Object in, int targetSqlType, int scal
}
break;
//#endif
case Types.BOOLEAN:
case Types.BIT:
setBoolean(parameterIndex, castToBoolean(in));
setBoolean(parameterIndex, BooleanTypeUtil.castToBoolean(in));
break;
case Types.BINARY:
case Types.VARBINARY:
@@ -877,74 +872,6 @@ private static BigDecimal castToBigDecimal(final Object in, final int scale) thr
throw cannotCastException(in.getClass().getName(), "BigDecimal");
}
private static boolean castToBoolean(final Object in) throws SQLException {
if (in instanceof Boolean) {
return (Boolean) in;
}
if (in instanceof String) {
// Leading or trailing whitespace is ignored, and case does not matter.
final String strval = ((String) in).trim();
Boolean value = booleanTypeFromString(strval);
if (value == null) {
throw cannotCastException("String", "boolean");
}
return value;
}
if (in instanceof Character) {
Boolean value = null;
final Character charval = (Character) in;
if ('1' == charval || 't' == charval || 'T' == charval
|| 'y' == charval || 'Y' == charval) {
value = Boolean.TRUE;
} else if ('0' == charval || 'f' == charval || 'F' == charval
|| 'n' == charval || 'N' == charval) {
value = Boolean.FALSE;
} else if (value == null) {
throw cannotCastException("Character", "boolean");
}
return value;
}
if (in instanceof BigDecimal) {
return ((BigDecimal) in).signum() != 0;
}
if (in instanceof Number) {
return ((Number) in).longValue() != 0L;
}
if (in instanceof java.util.Date) {
return ((java.util.Date) in).getTime() != 0L;
}
if (in instanceof Clob) {
final String strval = asString((Clob) in).trim();
Boolean value = booleanTypeFromString(strval);
if (value == null) {
throw cannotCastException("Clob", "boolean");
}
return value;
}
throw cannotCastException(in.getClass().getName(), "boolean");
}
/**
* Based on values accepted by the PostgreSQL server:
* https://www.postgresql.org/docs/current/static/datatype-boolean.html
*
* @param val String with the value to "parse"
* @return Boolean if any valid value match, null otherwise.
*/
private static Boolean booleanTypeFromString(final String val) {
if ("1".equals(val) || "true".equalsIgnoreCase(val)
|| "t".equalsIgnoreCase(val) || "yes".equalsIgnoreCase(val)
|| "y".equalsIgnoreCase(val) || "on".equalsIgnoreCase(val)) {
return Boolean.TRUE;
}
if ("0".equals(val) || "false".equalsIgnoreCase(val)
|| "f".equalsIgnoreCase(val) || "no".equalsIgnoreCase(val)
|| "n".equalsIgnoreCase(val) || "off".equalsIgnoreCase(val)) {
return Boolean.FALSE;
}
return null;
}
private static String castToString(final Object in) throws SQLException {
try {
if (in instanceof String) {
@@ -169,12 +169,10 @@ public ResultSetMetaData getMetaData() throws SQLException {
protected Object internalGetObject(int columnIndex, Field field) throws SQLException {
switch (getSQLType(columnIndex)) {
case Types.BOOLEAN:
case Types.BIT:
return getBoolean(columnIndex);
case Types.SQLXML:
return getSQLXML(columnIndex);
case Types.BIT:
// Also Types.BOOLEAN in JDBC3
return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
@@ -1918,6 +1916,29 @@ public String getString(int columnIndex) throws SQLException {
}
}
/**
* Retrieves the value of the designated column in the current row of this <code>ResultSet</code>
* object as a <code>boolean</code> in the Java programming language.
* <p>
* If the designated column has a Character datatype and is one of the following values: "1",
* "true", "t", "yes", "y" or "on", a value of <code>true</code> is returned. If the designated
* column has a Character datatype and is one of the following values: "0", "false", "f", "no",
* "n" or "off", a value of <code>false</code> is returned. Leading or trailing whitespace is
* ignored, and case does not matter.
* <p>
* If the designated column has a Numeric datatype and is a 1, a value of <code>true</code> is
* returned. If the designated column has a Numeric datatype and is a 0, a value of
* <code>false</code> is returned.
*
* @param columnIndex the first column is 1, the second is 2, ...
* @return the column value; if the value is SQL <code>NULL</code>, the value returned is
* <code>false</code>
* @exception SQLException if the columnIndex is not valid; if a database access error occurs or
* this method is called on a closed result set
* @see <a href="https://www.postgresql.org/docs/current/static/datatype-boolean.html">PostgreSQL
* Boolean Type</a>
*/
@Override
public boolean getBoolean(int columnIndex) throws SQLException {
checkResultSet(columnIndex);
if (wasNullFlag) {
@@ -1926,10 +1947,10 @@ public boolean getBoolean(int columnIndex) throws SQLException {
if (isBinary(columnIndex)) {
int col = columnIndex - 1;
return readDoubleValue(this_row[col], fields[col].getOID(), "boolean") == 1;
return BooleanTypeUtil.castToBoolean(readDoubleValue(this_row[col], fields[col].getOID(), "boolean"));
}
return toBoolean(getString(columnIndex));
return BooleanTypeUtil.castToBoolean(getString(columnIndex));
}
private static final BigInteger BYTEMAX = new BigInteger(Byte.toString(Byte.MAX_VALUE));
@@ -2440,6 +2461,7 @@ public String getString(String columnName) throws SQLException {
return getString(findColumn(columnName));
}
@Override
public boolean getBoolean(String columnName) throws SQLException {
return getBoolean(findColumn(columnName));
}
@@ -2759,28 +2781,6 @@ protected boolean isBinary(int column) {
// ----------------- Formatting Methods -------------------
public static boolean toBoolean(String s) {
if (s != null) {
s = s.trim();
if (s.equalsIgnoreCase("t") || s.equalsIgnoreCase("true") || s.equals("1")) {
return true;
}
if (s.equalsIgnoreCase("f") || s.equalsIgnoreCase("false") || s.equals("0")) {
return false;
}
try {
if (Double.parseDouble(s) == 1) {
return true;
}
} catch (NumberFormatException e) {
}
}
return false; // SQL NULL
}
private static final BigInteger INTMAX = new BigInteger(Integer.toString(Integer.MAX_VALUE));
private static final BigInteger INTMIN = new BigInteger(Integer.toString(Integer.MIN_VALUE));
@@ -107,6 +107,7 @@ public int hashCode() {
public final static PSQLState DATA_TYPE_MISMATCH = new PSQLState("42821");
public final static PSQLState UNDEFINED_FUNCTION = new PSQLState("42883");
public final static PSQLState INVALID_NAME = new PSQLState("42602");
public final static PSQLState CANNOT_COERCE = new PSQLState("42846");
public final static PSQLState OUT_OF_MEMORY = new PSQLState("53200");
public final static PSQLState OBJECT_NOT_IN_STATE = new PSQLState("55000");
Oops, something went wrong.

0 comments on commit 4942f7d

Please sign in to comment.