Functional Spec For Boolean Datatype

Revision Description Date Author
2.0 I abandoned the first attempt to implement a BOOLEAN datatype. This spec describes a second effort. May 9, 2007 Rick Hillegas

Overview

This spec describes the introduction of the ANSI BOOLEAN datatype. Derby 10.3 already partially supports this datatype:

In Target Release, Derby behavior changes as follows:

Terms

For the remainder of this spec, we use the following terms:

Incompatibilities

The following changes create incompatibilities with previous Derby releases. The community needs to decide whether these incompatibilities warrant bumping the release identifier's major number.

New SQL Syntax

We introduce new SQL syntax:

So, for instance, the following script is supported:


CREATE TABLE sample( boolCol BOOLEAN );

CREATE INDEX boolIndex on sample( boolCol );

INSERT INTO SAMPLE( boolCol ) VALUES
( true ),
( false ),
( null ),
( unknown );

select * from sample order by boolCol;

Casting Behavior

In Target Release, Derby implements ANSI casting behavior with respect to BOOLEANs. This is a change from Derby's behavior in release 10.3. The ANSI casting behavior is described in the SQL Standard, part 2, section 6.12 (<cast specification>). For details on the discrepancy between Derby 10.3 and the ANSI spec, see Appendix A.

Explicit Casts

Derby 10.3 lets you cast BOOLEAN to most other datatypes. However, ANSI only allows you to cast BOOLEANs to the string types CHAR, VARCHAR, LONG VARCHAR, and CLOB.

According to the SQL Standard, the result of casting a BOOLEAN to a string type is an uppercase string value "TRUE" or "FALSE". For fixed-length string types, this value is then padded with trailing spaces. A truncation error is raised if the fixed-length string type is too small. Derby 10.3 correctly pads and raises truncation errors. However, Derby 10.3 casts BOOLEAN to lowercase "true" and "false".

In Target Release, Derby behavior changes to conform to the SQL Standard:

In addition, in Target Release, only the string types and BOOLEAN itself may be cast to BOOLEAN. The reverse casts are also allowed and those are the only legal casts from BOOLEAN.

Implicit Casts

Derby 10.3 allows comparisons between BOOLEAN values and numbers. In these comparisons, the number is implicitly cast to a BOOLEAN: 0 becomes false and all other values become true. The SQL Standard does not allow these comparisons because the underlying cast is illegal.

In Target Release, Derby behavior changes to conform to the SQL Standard:

ODBC Metadata Queries

In Derby 10.3, the ODBC metadata queries depend on the ability to perform a non-ANSI cast from a BOOLEAN to an INT. These queries are rewritten in Target Release to use a CASE clause instead. E.g.:


select
case
  when boolcol then 1
  when not boolcol then 0
  else null
  end
...
;

DatabaseMetaData

Several DatabaseMetaData methods return datatype information:

All of these ResultSets have a column, DATA_TYPE, which contains the type ID of the described column/parameter/result. Another field in this row, TYPE_NAME, contains the server's name for the datatype of the described column/parameter/result. In Target Release, these columns contain the following values for BOOLEAN columns/parameters/results:

DatabaseMetaData type information
Column name Server running on jdk 1.3 Otherwise...
DATA_TYPE java.sql.Types.BIT java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN BOOLEAN

Appendix B describes the old behavior of these DatabaseMetaData methods.

ResultSetMetaData

Two ResultSetMetaData methods describe type information for the columns returned by queries:

These methods return the following values for BOOLEAN columns when either the client and server are at Derby Target Release or higher:

ResultSetMetaData: Client or server at Target Release
Method name Both client and server at Target Release Otherwise...
getColumnType() java.sql.Types.BOOLEAN java.sql.Types.SMALLINT
getColumnTypeName() BOOLEAN SMALLINT

Appendix C describes legacy behavior. Note that jdk 1.3 is not a supported platform for Target Release.

ParameterMetaData

JDK 1.4 introduced the ParameterMetaData interface with the following methods which describe type information for ? query parameters:

These methods return the following values for BOOLEAN parameters:

ParameterMetaData type information
Method name When client and server are at Target Release or higher Otherwise
getParameterType() java.sql.Types.BOOLEAN java.sql.Types.SMALLINT
getParameterTypeName() BOOLEAN SMALLINT

See Appendix D for a description of how the ParameterMetaData methods behave in earlier Derby releases.

JDBC getXXX() Methods

Appendix E describes the old behavior of getXXX() methods on BOOLEAN columns in ResultSets. Note that embedded and client/server behavior differ for two methods: getString() and getObject(). The getXXX() methods of ResultSet and CallableStatement continue to behave as described in Appendix E UNLESS both the client and server are at Target Release or higher. In that case, the old embedded behavior prevails and getString() and getObject() behave as follows:

getXXX() on BOOLEANs
When both client and server are at Target Release or higher
getString()
null null
false "false"
true "true"
getObject()
null null
false java.lang.Boolean( false )
true java.lang.Boolean( true )

JDBC setXXX() Methods

Appendix F describes the old behavior of setXXX() methods on BOOLEAN columns in PreparedStatements. If both the client and server are at Target Release, then setXXX() methods on PreparedStatements and CallableStatements conform to the embedded behavior described in Appendix F. Otherwise, these methods behave according to the Derby client and DB2JCC behavior described in Appendix F.

Upgrade

Soft Upgrade

After a soft-upgrade to Target Release, you see the following changes introduced by this spec:

Scenario Old behavior New behavior Customer needs to make these changes...
JDBC Behavior Datatype identification and getter/setter methods behaved as described in Appendix B, Appendix C, Appendix D Appendix E and Appendix F. Datatype identification and getter/setter methods behave differently if both the client and server are at Target Release. The customer's application may need to be recoded to expect the Target Release type identification and JDBC getter/setter behavior.
Casts from BOOLEAN BOOLEAN could be cast to various datatypes, contrary to the ANSI rules. In addition BOOLEANs were implicitly cast to numerics in comparisons with numbers. In addition casting a BOOLEAN to a string type resulted in a lowercase "true" or "false". ANSI casting rules enforced: BOOLEANs can only be cast to string types. BOOLEANs cannot be compared to numbers. The result of casting a BOOLEAN to a string type is an uppercase "TRUE" or "FALSE". The customer may need to rewrite her queries to eliminate non-ANSI casts and illegal comparisons of BOOLEANs to numbers.
ODBC Metadata Queries Relied on non-ANSI casting of BOOLEAN to INT. Uses CASE clauses to convert BOOLEAN to INT. The upgrade behavior of metadata queries is described on the Metadata and Upgrade wiki page. No action needed.

Hard Upgrade

After a hard-upgrade to Target Release, you also see the following behavior:

Scenario Old behavior New behavior Customer needs to make these changes...
BOOLEAN columns/arguments/returnValues Not recognized in old releases. The user could not create tables/functions/procedures with BOOLEAN columns/arguments/returnValues. Now you can create tables/functions/procedures with BOOLEAN columns/arguments/returnValues. No action needed.
Casts to BOOLEAN Explicit casts to BOOLEAN were not allowed. ANSI casting rules enforced: String types can be cast explicitly to BOOLEAN. No action needed.

Documentation

This new behavior will require changes to the user guides:

Release Notes

We need a release note for this feature. The release note should describe:

Appendix A: Old Casting Details

The following script demonstrates the BOOLEAN casting behavior in Derby 10.3:


--
-- supported
--
select
  cast (systemalias as SMALLINT),
  cast (systemalias as INTEGER),
  cast (systemalias as BIGINT),
  cast (systemalias as DECIMAL),
  cast (systemalias as NUMERIC),
  cast (systemalias as REAL),
  cast (systemalias as DOUBLE),
  cast (systemalias as FLOAT),
  cast (systemalias as CHAR(1)),
  cast (systemalias as VARCHAR(1)),
  cast (systemalias as LONG VARCHAR),
  cast (systemalias as CLOB),

  cast (systemalias as char(1) for bit data),
  cast (systemalias as varchar(1) for bit data),
  cast (systemalias as long varchar for bit data),
  xmlserialize( cast (systemalias as xml) as clob),
  cast (systemalias as BLOB)
from sys.sysaliases
where 0=1;


--
-- NOT supported
--
select
  cast (systemalias as date)
from sys.sysaliases
where 0=1;

select
  cast (systemalias as time)
from sys.sysaliases
where 0=1;

select
  cast (systemalias as timestamp)
from sys.sysaliases
where 0=1;

--
-- Result of casting booleans to character types
-- is a lowercase value although ANSI specifies uppercase.
-- Fixed length character types are correctly padded with
-- trailing spaces.
--
select
  cast (systemalias as CHAR(8)) || 'foo',
  cast (systemalias as VARCHAR(8)) || 'foo',
  cast (systemalias as LONG VARCHAR),
  cast (systemalias as CLOB)
from sys.sysaliases
where alias='SYSCS_IMPORT_TABLE';

--
-- Correctly raises truncation exceptions.
--
select
  cast (systemalias as CHAR(3))
from sys.sysaliases
where alias='SYSCS_IMPORT_TABLE';

select
  cast (systemalias as VARCHAR(3))
from sys.sysaliases
where alias='SYSCS_IMPORT_TABLE';

--
-- Comparing booleans to numbers. Should raise
-- a syntax error but Derby tolerates this syntax.
-- The number is implicitly cast to a boolean: 0 becomes
-- false and all other values become true.
--
select systemalias
from sys.sysaliases
where systemalias = 1;

select systemalias
from sys.sysaliases
where systemalias < -1;

The following table describes casts from BOOLEAN to target datatypes. Note the discrepancy between the ANSI casting behavior and the casting behavior observed in Derby 10.3.

Explicit Casts: X marks allowed casts
Target Datatype Derby 10.3 ANSI
from BOOLEAN to BIGINT X -
from BOOLEAN to BLOB X -
from BOOLEAN to CHAR X X
from BOOLEAN to CHAR FOR BIT DATA X -
from BOOLEAN to CLOB X X
from BOOLEAN to DATE - -
from BOOLEAN to DECIMAL X -
from BOOLEAN to DOUBLE X -
from BOOLEAN to DOUBLE PRECISION X -
from BOOLEAN to FLOAT X -
from BOOLEAN to INTEGER X -
from BOOLEAN to LONG VARCHAR X X
from BOOLEAN to LONG VARCHAR FOR BIT DATA X -
from BOOLEAN to NUMERIC X -
from BOOLEAN to REAL X -
from BOOLEAN to SMALLINT X -
from BOOLEAN to TIME - -
from BOOLEAN to TIMESTAMP - -
from BOOLEAN to VARCHAR X X
from BOOLEAN to VARCHAR FOR BIT DATA X -
from BOOLEAN to XML X -

The following table describes casts from source datatypes to BOOLEAN. Note the discrepancy between the ANSI casting behavior and the casting behavior observed in Derby 10.3.

Explicit Casts: X marks allowed casts
Target Datatype Derby 10.3 ANSI
from BIGINT to BOOLEAN - -
from BLOB to BOOLEAN - -
from CHAR to BOOLEAN - X
from CHAR FOR BIT DATA to BOOLEAN - -
from CLOB to BOOLEAN - X
from DATE to BOOLEAN - -
from DECIMAL to BOOLEAN - -
from DOUBLE to BOOLEAN - -
from DOUBLE PRECISION to BOOLEAN - -
from FLOAT to BOOLEAN - -
from INTEGER to BOOLEAN - -
from LONG VARCHAR to BOOLEAN - X
from LONG VARCHAR FOR BIT DATA to BOOLEAN - -
from NUMERIC to BOOLEAN - -
from REAL to BOOLEAN - -
from SMALLINT to BOOLEAN - -
from TIME to BOOLEAN - -
from TIMESTAMP to BOOLEAN - -
from VARCHAR to BOOLEAN - X
from VARCHAR FOR BIT DATA to BOOLEAN - -
from XML to BOOLEAN - -

Appendix B: Old DatabaseMetaData Behavior

The following table describes the values of the DATA_TYPE and TYPE_NAME fields returned by DatabaseMetaData.getColumns() for SYS.SYSALIASES.SYSTEMALIAS in various combinations of Derby clients, servers, and VMs. These are the results for Derby 10.2 and earlier.

DatabaseMetaData.getColumns()
Server / 1.3 Server / 1.4
Embedded
DATA_TYPE java.sql.Types.BIT
TYPE_NAME BOOLEAN
DATA_TYPE java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN
DerbyClient / 1.3
DATA_TYPE java.sql.Types.BIT
TYPE_NAME BOOLEAN
DATA_TYPE java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN
DerbyClient / 1.4
DATA_TYPE java.sql.Types.BIT
TYPE_NAME BOOLEAN
DATA_TYPE java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN
DB2JCC / 1.3
DATA_TYPE java.sql.Types.BIT
TYPE_NAME BOOLEAN
DATA_TYPE java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN
DB2JCC / 1.4
DATA_TYPE java.sql.Types.BIT
TYPE_NAME BOOLEAN
DATA_TYPE java.sql.Types.BOOLEAN
TYPE_NAME BOOLEAN

Appendix C: Old ResultSetMetaData Behavior

The following table describes the values of the getColumnType() and getColumnTypeName() methods of the ResultSetMetaData returned for the query "select systemalias from sys.sysaliases" in various combinations of Derby clients, servers, and VMs. These are the results for Derby 10.2 and earlier.

ResultSetMetaData methods
Server / 1.3 Server / 1.4
Embedded
getColumnType() java.sql.Types.BIT
getColumnTypeName() BOOLEAN
getColumnType() java.sql.Types.BOOLEAN
getColumnTypeName() BOOLEAN
DerbyClient / 1.3
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
DerbyClient / 1.4
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
DB2JCC / 1.3
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
DB2JCC / 1.4
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT
getColumnType() java.sql.Types.SMALLINT
getColumnTypeName() SMALLINT

Appendix D: Old ParameterMetaData Behavior

I do not know of any legal way for an old version of Derby to compile a SQL statement with a parameter which has type BOOLEAN. However, as of the writing of this spec, bug DERBY-2605 allows you to create user tables with BOOLEAN columns in the mainline. The following table describes the values of the getParameterType() and getParameterTypeName() methods of the ParameterMetaData returned for the statement "insert into foo( boolcol ) values ( ? )" thanks to this bug:

ParameterMetaData methods
Method results
Embedded
getParameterType() java.sql.Types.BOOLEAN
getParameterTypeName() BOOLEAN
Derby client
getParameterType() java.sql.Types.SMALLINT
getParameterTypeName() SMALLINT
DB2JCC client
getParameterType() java.sql.Types.SMALLINT
getParameterTypeName() SMALLINT

Appendix E: Old getXXX() Behavior

Here is how ResultSet getXXX() methods behave on BOOLEAN columns in Derby 10.3 and earlier. Note that the embedded and client/server behavior is identical except for getString() and getObject().

ResultSet.getXXX() behavior for BOOLEANs
Embedded on any VM Any client (Derby/DB2JCC) on any VM against server on any VM
getByte()
null 0
false 0
true 1
null 0
false 0
true 1
getShort()
null 0
false 0
true 1
null 0
false 0
true 1
getInt()
null 0
false 0
true 1
null 0
false 0
true 1
getLong()
null 0
false 0
true 1
null 0
false 0
true 1
getFloat()
null 0.0
false 0.0
true 1.0
null 0.0
false 0.0
true 1.0
getDouble()
null 0.0
false 0.0
true 1.0
null 0.0
false 0.0
true 1.0
getBigDecimal()
null null
false 0
true 1
null null
false 0
true 1
getBoolean()
null false
false false
true true
null false
false false
true true
getString()
null null
false "false"
true "true"
null null
false "0"
true "1"
getObject()
null null
false java.lang.Boolean( false )
true java.lang.Boolean( true )
null null
false java.lang.Integer( 0 )
true java.lang.Integer( 1 )

Appendix F: Old setXXX() Behavior

I do not know of any legal way for an old version of Derby to compile a SQL statement which inserts into a BOOLEAN column. However, as of the writing of this spec, bug DERBY-2605 allows you to create user tables with BOOLEAN columns in the mainline. The following table describes the behavior of various PreparedStatement setXXX() methods for the statement "insert into foo( boolcol ) values ( ? )", thanks to this bug. Derby embedded and client behavior mostly agree. DB2JCC behavior, however, diverges. Differences from the embedded behavior are marked in gold.

ResultSet.setXXX() behavior for BOOLEANs
Embedded Derby client DB2JCC client
setBigDecimal()
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 false
setByte()
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 false
Value set by client Value actually inserted
0 false
1 true
2 false
setDouble()
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 true
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 true
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 false
setFloat()
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 true
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 true
Value set by client Value actually inserted
0.0 false
1.0 true
2.0 false
setInt()
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 false
setLong()
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 false
setObject()
Value set by client Value actually inserted
new Integer( 0 ) false
new Integer( 1 ) true
new Integer( 2 ) true
"FALSE" false
"TRUE" true
"false" false
"true" true
new Boolean( false ) false
new Boolean( true ) true
Value set by client Value actually inserted
new Integer( 0 ) false
new Integer( 1 ) true
new Integer( 2 ) true
"FALSE" false
"TRUE" true
"false" false
"true" true
new Boolean( false ) false
new Boolean( true ) true
Value set by client Value actually inserted
new Integer( 0 ) false
new Integer( 1 ) true
new Integer( 2 ) false
"FALSE" ERROR
"TRUE" ERROR
"false" ERROR
"true" ERROR
new Boolean( false ) false
new Boolean( true ) true
setShort()
Value set by client Value actually inserted
0 false
1 true
2 true
Value set by client Value actually inserted
0 false
1 true
2 false
Value set by client Value actually inserted
0 false
1 true
2 false
setString()
Value set by client Value actually inserted
"FALSE" false
"TRUE" true
"false" false
"true" true
"baffle" ERROR
Value set by client Value actually inserted
"FALSE" false
"TRUE" true
"false" false
"true" true
"baffle" ERROR
Value set by client Value actually inserted
"FALSE" ERROR
"TRUE" ERROR
"false" ERROR
"true" ERROR
"baffle" ERROR
setNull()
Type of null Value actually inserted
java.sql.Types.BIGINT null
java.sql.Types.BOOLEAN null
java.sql.Types.CHAR null
java.sql.Types.CLOB ERROR
java.sql.Types.DECIMAL null
java.sql.Types.DOUBLE null
java.sql.Types.FLOAT null
java.sql.Types.INTEGER null
java.sql.Types.JAVA_OBJECT ERROR
java.sql.Types.LONGVARCHAR null
java.sql.Types.NULL ERROR
java.sql.Types.SMALLINT null
java.sql.Types.TINYINT null
java.sql.Types.VARCHAR null
Type of null Value actually inserted
java.sql.Types.BIGINT null
java.sql.Types.BOOLEAN null
java.sql.Types.CHAR null
java.sql.Types.CLOB null
java.sql.Types.DECIMAL null
java.sql.Types.DOUBLE null
java.sql.Types.FLOAT null
java.sql.Types.INTEGER null
java.sql.Types.JAVA_OBJECT ERROR
java.sql.Types.LONGVARCHAR null
java.sql.Types.NULL null
java.sql.Types.SMALLINT null
java.sql.Types.TINYINT null
java.sql.Types.VARCHAR null
Type of null Value actually inserted
java.sql.Types.BIGINT null
java.sql.Types.BOOLEAN null
java.sql.Types.CHAR null
java.sql.Types.CLOB null
java.sql.Types.DECIMAL null
java.sql.Types.DOUBLE null
java.sql.Types.FLOAT null
java.sql.Types.INTEGER null
java.sql.Types.JAVA_OBJECT null
java.sql.Types.LONGVARCHAR null
java.sql.Types.NULL null
java.sql.Types.SMALLINT null
java.sql.Types.TINYINT null
java.sql.Types.VARCHAR null