From 84e5553a8813d09272786454b65b2fb710012af6 Mon Sep 17 00:00:00 2001 From: scott Date: Thu, 6 Jul 2023 00:23:11 -0400 Subject: [PATCH] manifold-sql change. - 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 https://github.com/xerial/sqlite-jdbc/issues/928 https://github.com/xerial/sqlite-jdbc/issues/933 --- .../api/{RawElement.java => BaseElement.java} | 4 +- .../main/java/manifold/sql/rt/api/Runner.java | 45 ++++-- .../java/manifold/sql/rt/api/TypeMap.java | 51 ------- .../manifold/sql/rt/api/ValueAccessor.java | 109 ++++++++++++++ .../sql/rt/jdbc/ArrayValueAccessor.java | 141 ++++++++++++++++++ .../sql/rt/jdbc/BigIntValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/BinaryValueAccessor.java | 63 ++++++++ .../sql/rt/jdbc/BitValueAccessor.java | 76 ++++++++++ .../sql/rt/jdbc/BlobValueAccessor.java | 77 ++++++++++ .../sql/rt/jdbc/BooleanValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/CharValueAccessor.java | 28 ++++ .../sql/rt/jdbc/ClobValueAccessor.java | 76 ++++++++++ .../sql/rt/jdbc/DataLinkValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/DateValueAccessor.java | 62 ++++++++ .../sql/rt/jdbc/DecimalValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/DefaultJdbcTypeMap.java | 127 ---------------- .../sql/rt/jdbc/DoubleValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/FloatValueAccessor.java | 28 ++++ .../sql/rt/jdbc/IntegerValueAccessor.java | 64 ++++++++ .../rt/jdbc/LongNvarcharValueAccessor.java | 28 ++++ .../rt/jdbc/LongVarBinaryValueAccessor.java | 28 ++++ .../sql/rt/jdbc/LongVarcharValueAccessor.java | 28 ++++ .../sql/rt/jdbc/NcharValueAccessor.java | 28 ++++ .../sql/rt/jdbc/NclobValueAccessor.java | 28 ++++ .../sql/rt/jdbc/NumericValueAccessor.java | 28 ++++ .../sql/rt/jdbc/NvarcharValueAccessor.java | 63 ++++++++ .../sql/rt/jdbc/OtherValueAccessor.java | 56 +++++++ .../sql/rt/jdbc/RealValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/RowIdValueAccessor.java | 60 ++++++++ .../sql/rt/jdbc/SmallIntValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/SqlXmlValueAccessor.java | 78 ++++++++++ .../sql/rt/jdbc/TimeValueAccessor.java | 62 ++++++++ .../jdbc/TimeWithTimeZoneValueAccessor.java | 57 +++++++ .../sql/rt/jdbc/TimestampValueAccessor.java | 62 ++++++++ .../TimestampWithTimeZoneValueAccessor.java | 61 ++++++++ .../sql/rt/jdbc/TinyIntValueAccessor.java | 64 ++++++++ .../sql/rt/jdbc/VarBinaryValueAccessor.java | 28 ++++ .../sql/rt/jdbc/VarcharValueAccessor.java | 63 ++++++++ .../services/manifold.sql.rt.api.TypeMap | 1 - .../manifold.sql.rt.api.ValueAccessor | 33 ++++ .../sql/schema/simple/TestSimple.java | 4 +- .../java/manifold/sql/api/DataElement.java | 15 +- .../sql/query/jdbc/JdbcQueryColumn.java | 23 ++- .../sql/query/jdbc/JdbcQueryParameter.java | 48 ++++-- .../sql/query/jdbc/JdbcQueryTable.java | 6 - .../sql/query/type/SqlIssueContainer.java | 5 + .../manifold/sql/query/type/SqlModel.java | 5 + .../sql/query/type/SqlParentType.java | 51 ++++++- .../java/manifold/sql/schema/api/Schema.java | 5 +- .../manifold/sql/schema/jdbc/JdbcSchema.java | 25 +++- .../sql/schema/jdbc/JdbcSchemaColumn.java | 13 +- 51 files changed, 2196 insertions(+), 259 deletions(-) rename manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/{RawElement.java => BaseElement.java} (91%) delete mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/TypeMap.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/ValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ArrayValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BigIntValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BinaryValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BitValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BlobValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BooleanValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/CharValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ClobValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DataLinkValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DateValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DecimalValueAccessor.java delete mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DefaultJdbcTypeMap.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DoubleValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/FloatValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/IntegerValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongNvarcharValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarBinaryValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarcharValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NcharValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NclobValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NumericValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NvarcharValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/OtherValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RealValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RowIdValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SmallIntValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SqlXmlValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeWithTimeZoneValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampWithTimeZoneValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TinyIntValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarBinaryValueAccessor.java create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarcharValueAccessor.java delete mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.TypeMap create mode 100644 manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.ValueAccessor diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/RawElement.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/BaseElement.java similarity index 91% rename from manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/RawElement.java rename to manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/BaseElement.java index 5feedc093..a5810c9fb 100644 --- a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/RawElement.java +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/BaseElement.java @@ -16,8 +16,10 @@ package manifold.sql.rt.api; -public interface RawElement +public interface BaseElement { String getName(); + int getPosition(); + int getSize(); boolean isNullable(); } diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/Runner.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/Runner.java index 37110258b..7a509cb18 100644 --- a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/Runner.java +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/Runner.java @@ -29,14 +29,16 @@ public class Runner { private final Class _queryClass; + private final int[] _jdbcParamTypes; private final Bindings _params; private final String _querySource; private final String _configName; private final Function _makeResult; - public Runner( Class queryClass, Bindings params, String querySource, String configName, Function makeResult ) + public Runner( Class queryClass, int[] jdbcParamTypes, Bindings params, String querySource, String configName, Function makeResult ) { _queryClass = queryClass; + _jdbcParamTypes = jdbcParamTypes; _params = params; _querySource = querySource; _configName = configName; @@ -59,7 +61,8 @@ public Iterable run() int i = 0; for( Object param : _params.values() ) { - ps.setObject( ++i, param ); + ValueAccessor accessor = ValueAccessor.get( _jdbcParamTypes[i] ); + accessor.setParameter( ps, ++i, param ); } try( ResultSet resultSet = ps.executeQuery() ) { @@ -87,8 +90,6 @@ private void rip( ResultSet resultSet ) { try { - TypeMap typeMap = TypeMap.findFirst(); - ResultSetMetaData metaData = resultSet.getMetaData(); for( boolean isOnRow = resultSet.next(); isOnRow; isOnRow = resultSet.next() ) { @@ -96,16 +97,9 @@ private void rip( ResultSet resultSet ) for( int i = 1; i <= metaData.getColumnCount(); i++ ) { String column = metaData.getColumnLabel( i ); - int datatype = metaData.getColumnType( i ); - Class type = typeMap.getType( new ResultColumn( metaData, i ), datatype ); - if( type.isPrimitive() ) - { - row.put( column, resultSet.getObject( i ) ); - } - else - { - row.put( column, resultSet.getObject( i, type ) ); - } + ValueAccessor accessor = ValueAccessor.get( metaData.getColumnType( i ) ); + Object value = accessor.getRowValue( resultSet, new ResultColumn( metaData, i ) ); + row.put( column, value ); } _results.add( _makeResult.apply( row ) ); } @@ -123,7 +117,7 @@ public Iterator iterator() return _results.iterator(); } - private class ResultColumn implements RawElement + private class ResultColumn implements BaseElement { private final ResultSetMetaData _metaData; private final int _i; @@ -147,12 +141,31 @@ public String getName() } } + @Override + public int getPosition() + { + return _i; + } + @Override public boolean isNullable() { try { - return _metaData.isNullable( _i ) != ResultSetMetaData.columnNoNulls; + return _metaData.isNullable( _i ) == ResultSetMetaData.columnNullable; + } + catch( SQLException e ) + { + throw ManExceptionUtil.unchecked( e ); + } + } + + @Override + public int getSize() + { + try + { + return _metaData.getPrecision( _i ); } catch( SQLException e ) { diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/TypeMap.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/TypeMap.java deleted file mode 100644 index a95417ba2..000000000 --- a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/TypeMap.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023 - Manifold Systems LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package manifold.sql.rt.api; - -import manifold.rt.api.util.ServiceUtil; -import manifold.util.concurrent.LocklessLazyVar; - -import java.util.HashSet; -import java.util.Set; - -public interface TypeMap -{ - LocklessLazyVar> PROVIDERS = - LocklessLazyVar.make( () -> { - Set registered = new HashSet<>(); - ServiceUtil.loadRegisteredServices( registered, TypeMap.class, TypeMap.class.getClassLoader() ); - return registered; - } ); - - static TypeMap findFirst() - { - return TypeMap.PROVIDERS.get().stream() - .filter( tm -> tm.getPriority() == Priority.High ) - .findFirst().orElse( - TypeMap.PROVIDERS.get().stream().filter( tm -> tm.getPriority() != Priority.High ) - .findFirst().orElseThrow( () -> new IllegalStateException( "Missing TypeMap" ) ) ); - } - - enum Priority {Low, High} - - Class getType( RawElement column, int type ); - - default Priority getPriority() - { - return Priority.High; - } -} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/ValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/ValueAccessor.java new file mode 100644 index 000000000..ad424d665 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/api/ValueAccessor.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.api; + +import manifold.rt.api.util.ServiceUtil; +import manifold.util.concurrent.LocklessLazyVar; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Each implementation of this interface handles a JDBC type from {@link java.sql.Types} and must be registered as a Java + * Service Provider. Manifold provides default implementations that are suitable for most use-cases, however these implementations + * can be overridden by returning a higher priority from {@link #getPriority()}. + *

+ * This interface performs the following:
+ * - resolves the Java type corresponding with the JDBC type from {@link java.sql.Types}
+ * - sets query parameter values
+ * - gets query result values
+ *
+ */ +public interface ValueAccessor +{ + LocklessLazyVar> ACCESSORS = + LocklessLazyVar.make( () -> { + Set registered = new HashSet<>(); + ServiceUtil.loadRegisteredServices( registered, ValueAccessor.class, ValueAccessor.class.getClassLoader() ); + return registered; + } ); + + LocklessLazyVar> BY_JDBC_TYPE = + LocklessLazyVar.make( () -> { + Map map = new HashMap<>(); + for( ValueAccessor acc : ACCESSORS.get() ) + { + int jdbcType = acc.getJdbcType(); + ValueAccessor existing = map.get( jdbcType ); + if( existing == null || existing.getPriority() < acc.getPriority() ) + { + map.put( jdbcType, acc ); + } + } + return map; + } ); + + static ValueAccessor get( int jdbcType ) + { + return BY_JDBC_TYPE.get().get( jdbcType ); + } + + /** + * @return The {@link java.sql.Types} id this accessor handles. + */ + int getJdbcType(); + + /** + * Greater = higher priority. Higher priority overrides lower. Default implementations are lowest priority. They can be + * overridden. + */ + default int getPriority() + { + return Integer.MIN_VALUE; + } + + /** + * @return The resulting type of the value in Java code. Note this type may not correspond with SQL-to-Java type mappings + * from the JDBC specification. For instance, although {@code java.sql.Types#CLOB} maps to {@code java.sql.CLOB} (appendix + * table B.3 from the JDBC 4.2 specification) the actual type generated for {@code CLOB} is {@code String}. + */ + Class getJavaType( BaseElement elem ); + + /** + * Returns a query result value corresponding with a {@code elem} from {@code rs}. + * @param rs The result set containing rows of column values + * @param elem The query column from which to find a value + * @return The value corresponding with {@code elem}. Note, the type of the value must match the Java type returned from + * {@code elem.getType()}. + * @throws SQLException + */ + Object getRowValue( ResultSet rs, BaseElement elem ) throws SQLException; + + /** + * Sets the query parameter value corresponding with {@code pos}. + * @param ps The prepared statement containing the parameterized query. + * @param pos The index of the parameter, beginning with 1. + * @param value The value of the parameter + * @throws SQLException + */ + void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException; +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ArrayValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ArrayValueAccessor.java new file mode 100644 index 000000000..e2627d4dd --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ArrayValueAccessor.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; +import manifold.util.PrimitiveUtil; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.*; +import java.time.*; +import java.util.Calendar; +import java.util.LinkedHashMap; +import java.util.Map; + +public class ArrayValueAccessor implements ValueAccessor +{ + private static final Map, String> COMPONENT_TYPES = + new LinkedHashMap, String>() + {{ + put( Boolean.class, "BOOLEAN" ); + put( Byte.class, "TINYINT" ); + put( Short.class, "SMALLINT" ); + put( Integer.class, "INTEGER" ); + put( Long.class, "BIGINT" ); + put( Float.class, "REAL" ); + put( Double.class, "DOUBLE" ); + put( BigInteger.class, "NUMERIC" ); + put( BigDecimal.class, "NUMERIC" ); + put( Timestamp.class, "TIMESTAMP" ); + put( Instant.class, "TIMESTAMP" ); + put( LocalDateTime.class, "TIMESTAMP" ); + put( java.util.Date.class, "TIMESTAMP" ); + put( Calendar.class, "TIMESTAMP" ); + put( OffsetDateTime.class, "TIMESTAMP_WITH_TIMEZONE" ); + put( Time.class, "TIME" ); + put( LocalTime.class, "TIME" ); + put( OffsetTime.class, "TIME_WITH_TIMEZONE" ); + put( java.sql.Date.class, "DATE" ); + put( LocalDate.class, "DATE" ); + put( URL.class, "DATALINK" ); + put( String.class, "VARCHAR" ); + put( byte[].class, "VARBINARY" ); + }}; + + @Override + public int getJdbcType() + { + return Types.ARRAY; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return Object.class; + } + + @Override + public Object getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Array array = rs.getArray( elem.getPosition() ); + if( array == null ) + { + return null; + } + + try + { + return array.getArray(); + } + finally + { + array.free(); + } + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object array ) throws SQLException + { + if( array == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else + { + Class arrayType = array.getClass(); + if( !arrayType.isArray() ) + { + throw new SQLException( "Expecting array type, but found: " + arrayType.getTypeName() ); + } + + if( arrayType.isPrimitive() ) + { + // For some reason createArrayOf() only takes Object[], not primitive arrays, + // so we have to make a boxed version of a primitive array + array = makeBoxedArray( array, arrayType ); + arrayType = array.getClass(); + } + + Class componentType = arrayType.getComponentType(); + String jdbcType = COMPONENT_TYPES.get( componentType ); + Array jdbcArray = ps.getConnection().createArrayOf( jdbcType, (Object[])array ); + try + { + ps.setArray( pos, jdbcArray ); + } + finally + { + jdbcArray.free(); + } + } + } + + private Object[] makeBoxedArray( Object primitiveArray, Class valueClass ) + { + Class boxedType = PrimitiveUtil.getBoxedType( valueClass ); + int len = java.lang.reflect.Array.getLength( primitiveArray ); + Object[] boxedArray = (Object[])java.lang.reflect.Array.newInstance( boxedType, len ); + for( int i = 0; i < len; i++ ) + { + boxedArray[i] = java.lang.reflect.Array.get( primitiveArray, i ); + } + return boxedArray; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BigIntValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BigIntValueAccessor.java new file mode 100644 index 000000000..657e0b0f6 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BigIntValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class BigIntValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.BIGINT; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Long.class : long.class; + } + + @Override + public Long getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + long value = rs.getLong( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Long ) + { + ps.setLong( pos, (long)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BinaryValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BinaryValueAccessor.java new file mode 100644 index 000000000..d46eb08cc --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BinaryValueAccessor.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class BinaryValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.BINARY; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return byte[].class; + } + + @Override + public byte[] getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getBytes( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof byte[] ) + { + ps.setBytes( pos, (byte[])value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BitValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BitValueAccessor.java new file mode 100644 index 000000000..d4afc8bb1 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BitValueAccessor.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class BitValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.BIT; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + if( elem.getSize() > 1 ) + { + return byte[].class; + } + return elem.isNullable() ? Boolean.class : boolean.class; + } + + @Override + public Object getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + if( elem.getSize() > 1 ) + { + return rs.getBytes( elem.getPosition() ); + } + boolean value = rs.getBoolean( elem.getPosition() ); + return !value && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof byte[] ) + { + ps.setBytes( pos, (byte[])value ); + } + else if( value instanceof Boolean ) + { + ps.setBoolean( pos, (boolean)value ); + } + else + { + ps.setObject( pos, value, Types.BOOLEAN ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BlobValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BlobValueAccessor.java new file mode 100644 index 000000000..0ae8be8e1 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BlobValueAccessor.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.io.ByteArrayInputStream; +import java.sql.*; + +public class BlobValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.BLOB; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return byte[].class; + } + + @Override + public byte[] getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Blob blob = rs.getBlob( elem.getPosition() ); + if( blob == null ) + { + return null; + } + + try + { + return blob.getBytes( 1, (int)blob.length() ); + } + finally + { + blob.free(); + } + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof byte[] ) + { + byte[] bytes = (byte[])value; + ByteArrayInputStream stream = new ByteArrayInputStream( bytes ); + ps.setBinaryStream( pos, stream, bytes.length ); + + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BooleanValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BooleanValueAccessor.java new file mode 100644 index 000000000..fd522446c --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/BooleanValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class BooleanValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.BOOLEAN; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Boolean.class : boolean.class; + } + + @Override + public Boolean getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + boolean value = rs.getBoolean( elem.getPosition() ); + return !value && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Boolean ) + { + ps.setBoolean( pos, (boolean)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/CharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/CharValueAccessor.java new file mode 100644 index 000000000..238f2f914 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/CharValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class CharValueAccessor extends VarcharValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.CHAR; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ClobValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ClobValueAccessor.java new file mode 100644 index 000000000..db82b5cf4 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/ClobValueAccessor.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.io.StringReader; +import java.sql.*; + +public class ClobValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.CLOB; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return String.class; + } + + @Override + public String getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Clob clob = rs.getClob( elem.getPosition() ); + if( clob == null ) + { + return null; + } + + try + { + return clob.getSubString( 1, (int)clob.length() ); + } + finally + { + clob.free(); + } + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof String ) + { + String string = (String)value; + StringReader reader = new StringReader( string ); + ps.setCharacterStream( pos, reader, string.length() ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DataLinkValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DataLinkValueAccessor.java new file mode 100644 index 000000000..ef747561a --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DataLinkValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.net.URL; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class DataLinkValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.DATALINK; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return URL.class; + } + + @Override + public URL getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getURL( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof URL ) + { + ps.setURL( pos, (URL)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DateValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DateValueAccessor.java new file mode 100644 index 000000000..4642b2184 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DateValueAccessor.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; +import java.time.LocalDate; + +public class DateValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.DATE; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return LocalDate.class; + } + + @Override + public LocalDate getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Date date = rs.getDate( elem.getPosition() ); + return date == null ? null : date.toLocalDate(); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof LocalDate ) + { + ps.setDate( pos, Date.valueOf( (LocalDate)value ) ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DecimalValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DecimalValueAccessor.java new file mode 100644 index 000000000..7dbdc5b43 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DecimalValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class DecimalValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.DECIMAL; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return BigDecimal.class; + } + + @Override + public BigDecimal getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getBigDecimal( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof BigDecimal ) + { + ps.setBigDecimal( pos, (BigDecimal)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DefaultJdbcTypeMap.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DefaultJdbcTypeMap.java deleted file mode 100644 index 040d38b59..000000000 --- a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DefaultJdbcTypeMap.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2023 - Manifold Systems LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package manifold.sql.rt.jdbc; - -import manifold.sql.rt.api.TypeMap; -import manifold.sql.rt.api.RawElement; - -import java.math.BigDecimal; -import java.net.URL; -import java.sql.*; -import java.time.OffsetDateTime; - -import static java.sql.Types.*; - -public class DefaultJdbcTypeMap implements TypeMap -{ - @Override - public Class getType( RawElement column, int type ) - { - switch( type ) - { - case BIT: - case BOOLEAN: - return column.isNullable() ? Boolean.class : boolean.class; - - case TINYINT: - case SMALLINT: - case INTEGER: - return column.isNullable() ? Integer.class : int.class; - - case BIGINT: - return column.isNullable() ? Long.class : long.class; - - case REAL: - return column.isNullable() ? Float.class : float.class; - - case FLOAT: - case DOUBLE: - return column.isNullable() ? Double.class : double.class; - - case NUMERIC: - case DECIMAL: - return BigDecimal.class; - - case CHAR: - case VARCHAR: - case LONGVARCHAR: - case NCHAR: - case NVARCHAR: - case LONGNVARCHAR: - return String.class; - - case DATALINK: - return URL.class; - - case REF_CURSOR: - return ResultSet.class; - - case SQLXML: - return java.sql.SQLXML.class; - - case ROWID: - return RowId.class; - - case DATE: - return Date.class; - case TIME: - return Time.class; - case TIMESTAMP: - return Timestamp.class; - - case BINARY: - case VARBINARY: - case LONGVARBINARY: - return byte[].class; - - case BLOB: - return Blob.class; - case CLOB: - return Clob.class; - case NCLOB: - return NClob.class; - - case NULL: - case JAVA_OBJECT: - case DISTINCT: - case OTHER: - return Object.class; - - case STRUCT: - return java.sql.Struct.class; - - case ARRAY: - return Array.class; - - case REF: - return Ref.class; - - case TIME_WITH_TIMEZONE: - return java.time.OffsetTime.class; - - case TIMESTAMP_WITH_TIMEZONE: - return OffsetDateTime.class; - } - throw new IllegalStateException( "Unknown type: " + type ); - } - - @Override - public Priority getPriority() - { - return Priority.Low; - } -} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DoubleValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DoubleValueAccessor.java new file mode 100644 index 000000000..055916225 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/DoubleValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class DoubleValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.DOUBLE; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Double.class : double.class; + } + + @Override + public Double getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + double value = rs.getDouble( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Double ) + { + ps.setDouble( pos, (double)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/FloatValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/FloatValueAccessor.java new file mode 100644 index 000000000..76612248d --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/FloatValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class FloatValueAccessor extends DoubleValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.FLOAT; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/IntegerValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/IntegerValueAccessor.java new file mode 100644 index 000000000..7d3e3d840 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/IntegerValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class IntegerValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.INTEGER; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Integer.class : int.class; + } + + @Override + public Integer getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + int value = rs.getInt( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Integer ) + { + ps.setInt( pos, (int)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongNvarcharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongNvarcharValueAccessor.java new file mode 100644 index 000000000..3b545dd3e --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongNvarcharValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class LongNvarcharValueAccessor extends NvarcharValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.LONGNVARCHAR; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarBinaryValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarBinaryValueAccessor.java new file mode 100644 index 000000000..2a783c60c --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarBinaryValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class LongVarBinaryValueAccessor extends BinaryValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.LONGVARBINARY; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarcharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarcharValueAccessor.java new file mode 100644 index 000000000..9054bfc5e --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/LongVarcharValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class LongVarcharValueAccessor extends VarcharValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.LONGVARCHAR; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NcharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NcharValueAccessor.java new file mode 100644 index 000000000..0665cce45 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NcharValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class NcharValueAccessor extends NvarcharValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.NCHAR; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NclobValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NclobValueAccessor.java new file mode 100644 index 000000000..1f40844fa --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NclobValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.*; + +public class NclobValueAccessor extends ClobValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.NCLOB; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NumericValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NumericValueAccessor.java new file mode 100644 index 000000000..80f8dd45d --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NumericValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class NumericValueAccessor extends DecimalValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.NUMERIC; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NvarcharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NvarcharValueAccessor.java new file mode 100644 index 000000000..9880cf3a5 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/NvarcharValueAccessor.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class NvarcharValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.NVARCHAR; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return String.class; + } + + @Override + public String getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getNString( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof String ) + { + ps.setNString( pos, (String)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/OtherValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/OtherValueAccessor.java new file mode 100644 index 000000000..18e3f3b12 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/OtherValueAccessor.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; + +public class OtherValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.OTHER; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return Object.class; + } + + @Override + public Object getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getObject( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else + { + ps.setObject( pos, value ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RealValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RealValueAccessor.java new file mode 100644 index 000000000..09b52553d --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RealValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class RealValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.REAL; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Float.class : float.class; + } + + @Override + public Float getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + float value = rs.getFloat( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Float ) + { + ps.setFloat( pos, (float)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RowIdValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RowIdValueAccessor.java new file mode 100644 index 000000000..10c765982 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/RowIdValueAccessor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; + +public class RowIdValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.ROWID; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return RowId.class; + } + + @Override + public RowId getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getRowId( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof RowId ) + { + ps.setRowId( pos, (RowId)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SmallIntValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SmallIntValueAccessor.java new file mode 100644 index 000000000..af69db4ea --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SmallIntValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class SmallIntValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.SMALLINT; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Short.class : short.class; + } + + @Override + public Short getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + short value = rs.getShort( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Short ) + { + ps.setShort( pos, (short)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SqlXmlValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SqlXmlValueAccessor.java new file mode 100644 index 000000000..f4955109e --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/SqlXmlValueAccessor.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; + +public class SqlXmlValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.SQLXML; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return String.class; + } + + @Override + public String getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + SQLXML sqlxml = rs.getSQLXML( elem.getPosition() ); + if( sqlxml == null ) + { + return null; + } + + try + { + return sqlxml.getString(); + } + finally + { + sqlxml.free(); + } + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else + { + SQLXML sqlxml = ps.getConnection().createSQLXML(); + try + { + sqlxml.setString( value.toString() ); + ps.setSQLXML( pos, sqlxml ); + } + finally + { + sqlxml.free(); + } + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeValueAccessor.java new file mode 100644 index 000000000..ed48900d4 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeValueAccessor.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; +import java.time.LocalTime; + +public class TimeValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.TIME; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return LocalTime.class; + } + + @Override + public LocalTime getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Time time = rs.getTime( elem.getPosition() ); + return time == null ? null : time.toLocalTime(); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof LocalTime ) + { + ps.setTime( pos, Time.valueOf( (LocalTime)value ) ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeWithTimeZoneValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeWithTimeZoneValueAccessor.java new file mode 100644 index 000000000..a0b890de9 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimeWithTimeZoneValueAccessor.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; +import java.time.OffsetTime; + +public class TimeWithTimeZoneValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.TIME_WITH_TIMEZONE; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return OffsetTime.class; + } + + @Override + public OffsetTime getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getObject( elem.getPosition(), OffsetTime.class ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampValueAccessor.java new file mode 100644 index 000000000..12c9b6117 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampValueAccessor.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.*; +import java.time.Instant; + +public class TimestampValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.TIMESTAMP; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return Instant.class; + } + + @Override + public Instant getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + Timestamp timestamp = rs.getTimestamp( elem.getPosition() ); + return timestamp == null ? null : timestamp.toInstant(); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Instant ) + { + ps.setTimestamp( pos, Timestamp.from( (Instant)value ) ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampWithTimeZoneValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampWithTimeZoneValueAccessor.java new file mode 100644 index 000000000..7300622b4 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TimestampWithTimeZoneValueAccessor.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.OffsetDateTime; +import java.time.OffsetTime; + +public class TimestampWithTimeZoneValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.TIMESTAMP_WITH_TIMEZONE; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return OffsetDateTime.class; + } + + @Override + public OffsetTime getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getObject( elem.getPosition(), OffsetTime.class ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TinyIntValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TinyIntValueAccessor.java new file mode 100644 index 000000000..40de50359 --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/TinyIntValueAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class TinyIntValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.TINYINT; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return elem.isNullable() ? Byte.class : byte.class; + } + + @Override + public Byte getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + byte value = rs.getByte( elem.getPosition() ); + return value == 0 && rs.wasNull() ? null : value; + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof Byte ) + { + ps.setByte( pos, (byte)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarBinaryValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarBinaryValueAccessor.java new file mode 100644 index 000000000..8ee6ccc8b --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarBinaryValueAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import java.sql.Types; + +public class VarBinaryValueAccessor extends BinaryValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.VARBINARY; + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarcharValueAccessor.java b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarcharValueAccessor.java new file mode 100644 index 000000000..0b8222aea --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/java/manifold/sql/rt/jdbc/VarcharValueAccessor.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 - Manifold Systems LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package manifold.sql.rt.jdbc; + +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +public class VarcharValueAccessor implements ValueAccessor +{ + @Override + public int getJdbcType() + { + return Types.VARCHAR; + } + + @Override + public Class getJavaType( BaseElement elem ) + { + return String.class; + } + + @Override + public String getRowValue( ResultSet rs, BaseElement elem ) throws SQLException + { + return rs.getString( elem.getPosition() ); + } + + @Override + public void setParameter( PreparedStatement ps, int pos, Object value ) throws SQLException + { + if( value == null ) + { + ps.setNull( pos, getJdbcType() ); + } + else if( value instanceof String ) + { + ps.setString( pos, (String)value ); + } + else + { + ps.setObject( pos, value, getJdbcType() ); + } + } +} diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.TypeMap b/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.TypeMap deleted file mode 100644 index ad9ff82de..000000000 --- a/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.TypeMap +++ /dev/null @@ -1 +0,0 @@ -manifold.sql.rt.jdbc.DefaultJdbcTypeMap diff --git a/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.ValueAccessor b/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.ValueAccessor new file mode 100644 index 000000000..257920c7f --- /dev/null +++ b/manifold-deps-parent/manifold-sql-rt/src/main/resources/META-INF/services/manifold.sql.rt.api.ValueAccessor @@ -0,0 +1,33 @@ +manifold.sql.rt.jdbc.ArrayValueAccessor +manifold.sql.rt.jdbc.BinaryValueAccessor +manifold.sql.rt.jdbc.BitValueAccessor +manifold.sql.rt.jdbc.BlobValueAccessor +manifold.sql.rt.jdbc.BooleanValueAccessor +manifold.sql.rt.jdbc.CharValueAccessor +manifold.sql.rt.jdbc.ClobValueAccessor +manifold.sql.rt.jdbc.DataLinkValueAccessor +manifold.sql.rt.jdbc.DateValueAccessor +manifold.sql.rt.jdbc.DecimalValueAccessor +manifold.sql.rt.jdbc.DoubleValueAccessor +manifold.sql.rt.jdbc.FloatValueAccessor +manifold.sql.rt.jdbc.IntegerValueAccessor +manifold.sql.rt.jdbc.LongNvarcharValueAccessor +manifold.sql.rt.jdbc.BigIntValueAccessor +manifold.sql.rt.jdbc.LongVarBinaryValueAccessor +manifold.sql.rt.jdbc.LongVarcharValueAccessor +manifold.sql.rt.jdbc.NcharValueAccessor +manifold.sql.rt.jdbc.NclobValueAccessor +manifold.sql.rt.jdbc.NumericValueAccessor +manifold.sql.rt.jdbc.NvarcharValueAccessor +manifold.sql.rt.jdbc.OtherValueAccessor +manifold.sql.rt.jdbc.RealValueAccessor +manifold.sql.rt.jdbc.RowIdValueAccessor +manifold.sql.rt.jdbc.SmallIntValueAccessor +manifold.sql.rt.jdbc.SqlXmlValueAccessor +manifold.sql.rt.jdbc.TimestampValueAccessor +manifold.sql.rt.jdbc.TimestampWithTimeZoneValueAccessor +manifold.sql.rt.jdbc.TimeValueAccessor +manifold.sql.rt.jdbc.TimeWithTimeZoneValueAccessor +manifold.sql.rt.jdbc.TinyIntValueAccessor +manifold.sql.rt.jdbc.VarBinaryValueAccessor +manifold.sql.rt.jdbc.VarcharValueAccessor diff --git a/manifold-deps-parent/manifold-sql-test/src/test/java/manifold/sql/schema/simple/TestSimple.java b/manifold-deps-parent/manifold-sql-test/src/test/java/manifold/sql/schema/simple/TestSimple.java index 1480fa75a..19e67c1b8 100644 --- a/manifold-deps-parent/manifold-sql-test/src/test/java/manifold/sql/schema/simple/TestSimple.java +++ b/manifold-deps-parent/manifold-sql-test/src/test/java/manifold/sql/schema/simple/TestSimple.java @@ -82,7 +82,7 @@ public void testCommentQueryWithParameters() { /*[>MyQuery.sql<] Select * From purchase_order Where customer_id = :c_id */ StringBuilder actual = new StringBuilder(); - for( MyQuery.Row row : MyQuery.run( 2 ) ) + for( MyQuery.Row row : MyQuery.run( 2L ) ) { actual.append( row.getId() ).append( "," ).append( row.getCustomerId() ).append( "," ).append( row.getOrderDate() ).append( "\n" ); } @@ -101,7 +101,7 @@ public void testStringQueryWithParameters() "3,2,2023-09-08\n"; StringBuilder actual = new StringBuilder(); - for( auto row : query.run( 2 ) ) + for( auto row : query.run( 2L ) ) { actual.append( row.getId() ).append( "," ).append( row.getCustomerId() ).append( "," ).append( row.getOrderDate() ).append( "\n" ); } diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/api/DataElement.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/api/DataElement.java index 40cbe699d..66daa3886 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/api/DataElement.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/api/DataElement.java @@ -16,13 +16,18 @@ package manifold.sql.api; -import manifold.sql.rt.api.RawElement; +import manifold.sql.rt.api.BaseElement; +import manifold.sql.rt.api.ValueAccessor; -public interface DataElement extends RawElement +public interface DataElement extends BaseElement { Table getTable(); - int getPosition(); - Class getType(); - int getSize(); + int getJdbcType(); int getScale(); + + default Class getType() + { + ValueAccessor accessor = ValueAccessor.get( getJdbcType() ); + return accessor == null ? null : accessor.getJavaType( this ); + } } diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryColumn.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryColumn.java index d71504b53..b6ad55575 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryColumn.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryColumn.java @@ -30,7 +30,7 @@ public class JdbcQueryColumn implements QueryColumn private final JdbcSchemaColumn _schemaColumn; private final int _position; private final String _name; - private final Class _type; + private final int _jdbcType; private final int _size; private final int _scale; private final int _displaySize; @@ -52,14 +52,9 @@ public JdbcQueryColumn( int colIndex, JdbcQueryTable queryTable, ResultSetMetaDa _name = rsMetaData.getColumnLabel( colIndex ); _schemaColumn = _schemaTable == null ? null : _schemaTable.getColumn( rsMetaData.getColumnName( colIndex ) ); - int typeId = rsMetaData.getColumnType( colIndex ); - _type = queryTable.getTypeMap().getType( this, typeId ); - if( _type == null ) - { - throw new IllegalStateException( "Type is null for query: " + _queryTable.getName() + " column:" + _name ); - } + _jdbcType = rsMetaData.getColumnType( colIndex ); - _isNullable = rsMetaData.isNullable( colIndex ) != ResultSetMetaData.columnNoNulls; + _isNullable = rsMetaData.isNullable( colIndex ) == ResultSetMetaData.columnNullable; _size = rsMetaData.getPrecision( colIndex ); _scale = rsMetaData.getScale( colIndex ); @@ -81,6 +76,12 @@ public JdbcSchemaTable getSchemaTable() return _schemaTable; } + @Override + public int getJdbcType() + { + return _jdbcType; + } + public JdbcSchemaColumn getSchemaColumn() { return _schemaColumn; @@ -98,12 +99,6 @@ public String getName() return _name; } - @Override - public Class getType() - { - return _type; - } - @Override public boolean isNullable() { diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryParameter.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryParameter.java index 03ef6d8fc..950e16264 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryParameter.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryParameter.java @@ -28,7 +28,7 @@ public class JdbcQueryParameter implements QueryParameter private final JdbcQueryTable _queryTable; private final int _position; private final String _name; - private Class _type; + private final int _jdbcType; private final int _size; private final int _scale; private final boolean _isNullable; @@ -40,28 +40,50 @@ public JdbcQueryParameter( int paramIndex, String name, JdbcQueryTable queryTabl _name = name == null ? "p" + paramIndex : name; _queryTable = queryTable; - int typeId; + int jdbcType; try { - typeId = paramMetaData.getParameterType( paramIndex ); + jdbcType = paramMetaData.getParameterType( paramIndex ); } - catch( Exception e ) + catch( SQLException se ) { // (circus music) - // some drivers (SQLite since 3.42) require the parameter value to be set in the prepared statement BEFORE the - // call to getParameterType(), so the type can be obtained from the value (?!) instead of inferring the type from - // the parameter's context + // Some drivers don't provide query parameter types when the parameter's value is not set. For instance, depending + // on the version, SQLite will either return VARCHAR for all parameters or it will throw an exception when a parameter + // is not set, hence this catch block. + // preparedStatement.setString( paramIndex, "" ); - typeId = Types.OTHER; - _type = Object.class; + jdbcType = Types.OTHER; } - _type = _type == null ? queryTable.getTypeMap().getType( this, typeId ) : _type; + + _jdbcType = handleUnknownType( jdbcType ); _size = paramMetaData.getPrecision( paramIndex ); _scale = paramMetaData.getScale( paramIndex ); _isNullable = paramMetaData.isNullable( paramIndex ) != ParameterMetaData.parameterNoNulls; _isSigned = paramMetaData.isSigned( paramIndex ); } + private int handleUnknownType( int jdbcType ) + { + // Update: sqlite is flaky, see https://github.com/xerial/sqlite-jdbc/issues/928 + // + String databaseProductName = _queryTable.getSchema().getDatabaseProductName(); + if( "SQLite".equalsIgnoreCase( databaseProductName ) ) + { + // OTHER maps to java.lang.Object, which is less confusing than the VARCHAR type the sqlite driver assigns for all parameters. + // For instance, java.lang.Object enables param values like integer foreign key ids to be passed directly in, instead + // of confusing users with String type. + return Types.OTHER; // maps to java.lang.Object + } + return jdbcType; + } + + @Override + public int getJdbcType() + { + return _jdbcType; + } + public JdbcQueryTable getTable() { return _queryTable; @@ -79,12 +101,6 @@ public String getName() return _name; } - @Override - public Class getType() - { - return _type; - } - @Override public int getSize() { diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryTable.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryTable.java index 8cd6f51a5..07b92d78d 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryTable.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/jdbc/JdbcQueryTable.java @@ -21,7 +21,6 @@ import manifold.sql.query.type.SqlIssueContainer; import manifold.sql.query.type.SqlScope; import manifold.sql.rt.api.ConnectionProvider; -import manifold.sql.rt.api.TypeMap; import manifold.sql.rt.api.ConnectionNotifier; import manifold.sql.schema.api.Schema; import manifold.util.ManExceptionUtil; @@ -151,11 +150,6 @@ public List getParameters() return _parameters; } - public TypeMap getTypeMap() - { - return _scope.getSchema().getTypeMap(); - } - public SqlIssueContainer getIssues() { return _issues; diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlIssueContainer.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlIssueContainer.java index 0c01aa0ea..0bf074a96 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlIssueContainer.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlIssueContainer.java @@ -54,6 +54,11 @@ public List getErrors() return getIssues(); } + public void addIssue( IIssue.Kind kind, String msg ) + { + _issues.add( new SqlIssue( kind, msg ) ); + } + @SuppressWarnings("WeakerAccess") public void addIssues( List errors ) { diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlModel.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlModel.java index c1ce2ac2b..1b9e0dc9b 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlModel.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlModel.java @@ -124,6 +124,11 @@ public void updateFile( IFile file ) init(); } + void addIssue( IIssue.Kind kind, String msg ) + { + _issues.addIssue( kind, msg ); + } + void addIssue( Exception issue ) { _issues.addIssues( Collections.singletonList( issue ) ); diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlParentType.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlParentType.java index 512edb4a2..6bfb11938 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlParentType.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/query/type/SqlParentType.java @@ -21,9 +21,11 @@ import manifold.api.gen.*; import manifold.api.host.IModule; import manifold.internal.javac.HostKind; +import manifold.internal.javac.IIssue; import manifold.json.rt.api.*; import manifold.rt.api.*; import manifold.rt.api.util.ManClassUtil; +import manifold.sql.api.DataElement; import manifold.sql.query.api.QueryColumn; import manifold.sql.query.api.QueryParameter; import manifold.sql.query.api.QueryTable; @@ -36,6 +38,7 @@ import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import java.lang.reflect.Modifier; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import static manifold.api.gen.AbstractSrcClass.Kind.*; @@ -157,14 +160,32 @@ private void addRunMethod( SrcLinkedClass srcClass ) String configName = _model.getScope().getDbconfig().getName(); //noinspection unused String simpleName = srcClass.getSimpleName(); + //noinspection unused + String jdbcParamTypes = getJdbcParamTypes(); sb.append( - " return new Runner(Row.class, paramBindings, \"$query\", \"$configName\", " + + " return new Runner(Row.class, $jdbcParamTypes, paramBindings, \"$query\", \"$configName\", " + " rowBindings -> new Row() {public Bindings getBindings() { return rowBindings; }}" + " ).run();" ); method.body( sb.toString() ); srcClass.addMethod( method ); } + private String getJdbcParamTypes() + { + StringBuilder sb = new StringBuilder( "new int[]{"); + List parameters = getQuery().getParameters(); + for( int i = 0; i < parameters.size(); i++ ) + { + QueryParameter p = parameters.get( i ); + if( i > 0 ) + { + sb.append( "," ); + } + sb.append( p.getJdbcType() ); + } + return sb.append( "}" ).toString(); + } + private boolean isValueFragment( HostKind hostKind ) { switch( hostKind ) @@ -186,7 +207,13 @@ private void addRequiredParameters( AbstractSrcMethod method ) for( QueryParameter param: getQuery().getParameters() ) { - method.addParam( makeIdentifier( param.getName(), false ), new SrcType( param.getType() ) ); + java.lang.Class type = getType( param ); + if( type == null ) + { + // errant condition + type = Object.class; + } + method.addParam( makeIdentifier( param.getName(), false ), new SrcType( type ) ); } } @@ -229,7 +256,12 @@ private void importSchemaTypes( SrcLinkedClass srcClass ) private void addQueryGetter( SrcLinkedClass srcClass, QueryColumn column ) { - SrcType type = new SrcType( column.getType() ); + Class colType = getType( column ); + if( colType == null ) + { + return; + } + SrcType type = new SrcType( colType ); String name = column.getName(); String propName = makePascalCaseIdentifier( column.getName(), true ); SrcGetProperty getter = new SrcGetProperty( propName, type ) @@ -238,4 +270,17 @@ private void addQueryGetter( SrcLinkedClass srcClass, QueryColumn column ) addActualNameAnnotation( getter, name, true ); srcClass.addGetProperty( getter ).modifiers( Modifier.PUBLIC ); } + + private java.lang.Class getType( DataElement elem ) + { + java.lang.Class colType = elem.getType(); + if( colType == null ) + { + String label = elem instanceof QueryColumn ? "column" : "parameter"; + _model.addIssue( IIssue.Kind.Error, + "$label type unknown for query '${getQueryName()}', $label '${elem.getName()}', jdbcType '${elem.getJdbcType()}'" ); + return null; + } + return colType; + } } diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/api/Schema.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/api/Schema.java index 9f2b6937d..6a07784ec 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/api/Schema.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/api/Schema.java @@ -17,7 +17,6 @@ package manifold.sql.schema.api; import manifold.sql.rt.api.DbConfig; -import manifold.sql.rt.api.TypeMap; import java.util.Map; @@ -28,7 +27,9 @@ public interface Schema boolean hasTable( String name ); SchemaTable getTable( String name); Map getTables(); - TypeMap getTypeMap(); String getJavaTypeName( String name ); String getOriginalName( String pascalName ); + + String getDatabaseProductName(); + String getDatabaseProductVersion(); } diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchema.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchema.java index 12512d37f..43f66b7f8 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchema.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchema.java @@ -17,7 +17,6 @@ package manifold.sql.schema.jdbc; import manifold.sql.rt.api.ConnectionProvider; -import manifold.sql.rt.api.TypeMap; import manifold.sql.rt.api.DbConfig; import manifold.sql.schema.api.Schema; import manifold.sql.rt.api.ConnectionNotifier; @@ -37,7 +36,8 @@ public class JdbcSchema implements Schema private final Map _tables; private final Map _javaToName; private final Map _nameToJava; - private final TypeMap _typeMap; + private final String _dbProductName; + private final String _dbProductVersion; public JdbcSchema( DbConfig dbConfig ) { @@ -46,11 +46,13 @@ public JdbcSchema( DbConfig dbConfig ) _tables = new LinkedHashMap<>(); _javaToName = new LinkedHashMap<>(); _nameToJava = new LinkedHashMap<>(); - _typeMap = TypeMap.findFirst(); loadDriverClass( dbConfig ); ConnectionProvider cp = ConnectionProvider.findFirst(); try( Connection c = cp.getConnection( dbConfig ) ) { + _dbProductName = c.getMetaData().getDatabaseProductName(); + _dbProductVersion = c.getMetaData().getDatabaseProductVersion(); + build( c ); } catch( SQLException e ) @@ -160,11 +162,6 @@ public Map getTables() return _tables; } - public TypeMap getTypeMap() - { - return _typeMap; - } - public String getJavaTypeName( String name ) { return _nameToJava.get( name ); @@ -174,4 +171,16 @@ public String getOriginalName( String javaName ) { return _javaToName.get( javaName ); } + + @Override + public String getDatabaseProductName() + { + return _dbProductName; + } + + @Override + public String getDatabaseProductVersion() + { + return _dbProductVersion; + } } diff --git a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchemaColumn.java b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchemaColumn.java index bf6558796..cfa995afb 100644 --- a/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchemaColumn.java +++ b/manifold-deps-parent/manifold-sql/src/main/java/manifold/sql/schema/jdbc/JdbcSchemaColumn.java @@ -19,6 +19,7 @@ import manifold.sql.schema.api.SchemaColumn; import manifold.sql.schema.api.SchemaTable; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; @@ -28,7 +29,7 @@ public class JdbcSchemaColumn implements SchemaColumn private final JdbcSchemaTable _table; private final int _position; private final String _name; - private final Class _type; + private final int _jdbcType; private final boolean _isNullable; private final boolean _isGenerated; private final boolean _isPrimaryKeyPart; @@ -44,7 +45,8 @@ public JdbcSchemaColumn( int colIndex, JdbcSchemaTable jdbcSchemaTable, ResultSe _position = colIndex; _table = jdbcSchemaTable; _name = rs.getString( "COLUMN_NAME" ); - _isNullable = !"NO".equals( rs.getString( "IS_NULLABLE" ) ); + _jdbcType = rs.getInt( "DATA_TYPE" ); + _isNullable = rs.getInt( "NULLABLE" ) == DatabaseMetaData.columnNullable; _isGenerated = "YES".equals( rs.getString( "IS_GENERATEDCOLUMN" ) ); _isPrimaryKeyPart = primaryKey.contains( _name ); _isId = _isPrimaryKeyPart && primaryKey.size() == 1; @@ -52,9 +54,6 @@ public JdbcSchemaColumn( int colIndex, JdbcSchemaTable jdbcSchemaTable, ResultSe _size = rs.getInt( "COLUMN_SIZE" ); _decimalDigits = rs.getInt( "DECIMAL_DIGITS" ); _numPrecRadix = rs.getInt( "NUM_PREC_RADIX" ); - - int datatype = rs.getInt( "DATA_TYPE" ); - _type = jdbcSchemaTable.getSchema().getTypeMap().getType( this, datatype ); } @Override @@ -82,9 +81,9 @@ public boolean isNullable() } @Override - public Class getType() + public int getJdbcType() { - return _type; + return _jdbcType; } @Override