Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sqlite-jdbc does not comply with JDBC Specification regarding Appendix B.1, B.2, & B.3 #933

Open
rsmckinney opened this issue Jul 5, 2023 · 6 comments

Comments

@rsmckinney
Copy link

TLDR: Column values obtained from getObject should match the types from getColumnType(int), but they often do not.

Referring to JDBC 4.x Specification. (Same mappings exist in JDBC 3 Specification)

According to the spec:

Appendix Table B.3

JDBC Types Mapped to Java Object Types

The getObject methods for both the CallableStatement and ResultSet interfaces use the mapping in TABLE B-3

JDBC Type Java Object Type
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT Boolean
BOOLEAN Boolean
TINYINT Integer
SMALLINT Integer
INTEGER Integer
BIGINT Long
REAL Float
FLOAT Double
DOUBLE Double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
DISTINCT Object*
CLOB java.sql.Clob
BLOB java.sql.Blob
ARRAY java.sql.Array
STRUCT Struct or SQLData
REF java.sql.Ref
DATA_LINK java.net.URL
JAVA_OBJECT Object*
ROWID java.sql.RowId
NCHAR String
NVARCHAR String
LONGNVARCHAR String
NCLOB java.sql.NClob
SQLXML java.sql.SQLXML

The problem is calls to ResultSetMetadata#getColumnType(int) don't align with calls to ResultSet#getObject(int). Generally, Sqlite's implementation of getObject(int) does not adhere to Table B.3.

For instance, if I call getColumnType(int) and get back Types.DATE, the JDBC specification states that I can expect values of type java.sql.Date for that column. However, if I call getObject(int) for a row including that column, sqlite returns a value of type java.lang.String.

Similarly, calls to getColumn(int, Class<?>) are not covered. If getColumnType(int) returns Types.CLOB and getObject(int, Class<?>) is called with java.sql.Clob for the same column, a SQLFeatureNotSupportedException("not implemented by SQLite JDBC driver") results because getColumn(int, Class<?>) does not handle java.sql.Clob.

This behavior does not appear to comply with the specification (both 3 & 4), but more importantly this behavior complicates writing type-safe code generators that work with sqlite db connections.

@gotson
Copy link
Collaborator

gotson commented Jul 5, 2023

For instance, if I call getColumnType(int) and get back Types.DATE, the JDBC specification states that I can expect values of type java.sql.Date for that column. However, if I call getObject(int) for a row including that column, sqlite returns a value of type java.lang.String.

from what i recall when i looked into this, it's because the first one is looking at the table definition, while the second one is looking at the actual value for that row. There is no guarantee that they will match, since SQLite doesn't enforce types.

So you can define a column as 'datetime', and store a float inside.

@rsmckinney
Copy link
Author

rsmckinney commented Jul 5, 2023

@gotson That’s a separate issue re no type-safety.

In this case what I am saying is if a column is typed as Types.DATE, getObject(int) should always return a Java.sql.Date regardless of how the value was set. The method should attempt to coerce whatever is stored in that column to a Date. It should throw an exception if that can’t be done. If the user wants the value as some other data type, he can call getInt() etc. for specific coercions. This is how JDBC specifies getObject methods.

rsmckinney added a commit to manifold-systems/manifold that referenced this issue Jul 6, 2023
- added ValueAccessor SPI and default implementations, replaces TypeMap as a more effective means of resolving Java types corresponding with JDBC types, getting query result values, and setting parameter values
- added schema information for database product name and version to special case sqlite's type-safety issues
xerial/sqlite-jdbc#928
xerial/sqlite-jdbc#933
@gotson
Copy link
Collaborator

gotson commented Jul 6, 2023

@gotson That’s a separate issue re no type-safety.

In this case what I am saying is if a column is typed as Types.DATE, getObject(int) should always return a Java.sql.Date regardless of how the value was set. The method should attempt to coerce whatever is stored in that column to a Date. It should throw an exception if that can’t be done. If the user wants the value as some other data type, he can call getInt() etc. for specific coercions. This is how JDBC specifies getObject methods.

Could you provide a unit test showing the behaviour you think is wrong ?

@rsmckinney
Copy link
Author

I don't have time to write unit tests for sqlite. Here again from the OP is a complete description and example:

The problem is calls to ResultSetMetadata#getColumnType(int) don't align with calls to ResultSet#getObject(int). Generally, Sqlite's implementation of getObject(int) does not adhere to Table B.3.

For instance, if I call getColumnType(int) and get back Types.DATE, the JDBC specification states that I can expect values of type java.sql.Date for that column. However, if I call getObject(int) for a row including that column, sqlite returns a value of type java.lang.String.

@gotson
Copy link
Collaborator

gotson commented Jul 11, 2023

I don't have time to write unit tests for sqlite

If you don't, why would we? You're just throwing issues without much details or repro, then you are saying you don't have any time to invest in this.

@rsmckinney
Copy link
Author

There is plenty enough detail here for a “maintainer” to grok. If you aren’t interested in fixing stuff here, you might consider a different hobby.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants