Skip to content

Commit

Permalink
manifold-sql changes:
Browse files Browse the repository at this point in the history
- fixed multiline sql fragments
- sqlite incorrectly returns VARCHAR for schema column (during DatabaseMetadata#getColumns())
--- handling this by coercing query result values to string in generated accessor code
--- for longer term filed yet another sqlite bug: xerial/sqlite-jdbc#935
- other minor fixes
  • Loading branch information
rsmckinney committed Jul 11, 2023
1 parent 2e121d9 commit cf41e4d
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ public void testSimple()
@Test
public void testCommentQueryWithParameters()
{
/*[>MyQuery.sql<] Select * From purchase_order Where customer_id = :c_id */
/*[>MyQuery.sql<]
Select * From purchase_order Where customer_id = :c_id
*/
StringBuilder actual = new StringBuilder();
for( PurchaseOrder po : MyQuery.run( 2L ) )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public interface QueryTable extends Table

List<QueryParameter> getParameters();

Pair<SchemaTable, List<QueryColumn>> findPrimaryTable();
Pair<SchemaTable, List<QueryColumn>> findSelectedTable();
List<ForeignKeyQueryRef> findForeignKeyQueryRefs();

SqlIssueContainer getIssues();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ private void build( Connection c, List<ParamInfo> paramNames )
}

/**
* Find the table object that has all its non-null columns represented in the query columns.
* Find the selected table object that has all its non-null columns selected in the query columns.
* <p/>
* This feature enables, for example, [SELECT * FROM foo ...] query results to consist of Entities instead of column
* values.
* <p/>
* @return All query columns that correspond with the selected primary table, or null if no table is fully covered. The
* resulting columns are sufficient to create a valid instance of the entity corresponding with the table.
* @return All query columns that correspond with the primary selected table, or null if no table is fully covered. The
* resulting columns are sufficient to create a valid instance of the entity corresponding with the selected table.
*/
public Pair<SchemaTable, List<QueryColumn>> findPrimaryTable()
public Pair<SchemaTable, List<QueryColumn>> findSelectedTable()
{
Map<SchemaTable, List<QueryColumn>> map = queryColumnsBySchemaTable();
for( Map.Entry<SchemaTable, List<QueryColumn>> entry : map.entrySet() )
Expand All @@ -147,18 +147,18 @@ public Pair<SchemaTable, List<QueryColumn>> findPrimaryTable()
}

/**
* Of the query columns <i>not</i> corresponding with the primary table (if one exists, see findPrimaryTable() above), finds
* Of the query columns <i>not</i> corresponding with the selected table (if one exists, see findSelectedTable() above), finds
* the columns fully covering foreign keys, represented as {@link JdbcForeignKeyQueryRef}. The idea is to provide {@code get<foreign-key-ref>()}
* methods. For example, a {@code city_id} foreign key would result in a {@code getCityRef()} method return a {@code City}
* entity.
*/
public List<ForeignKeyQueryRef> findForeignKeyQueryRefs()
{
Map<String, QueryColumn> columns = getColumns();
Pair<SchemaTable, List<QueryColumn>> coveredTable = findPrimaryTable();
Pair<SchemaTable, List<QueryColumn>> coveredTable = findSelectedTable();
if( coveredTable != null )
{
// remove primary table columns from search
// remove selected table columns from search
coveredTable.getSecond().forEach( c -> columns.remove( c.getName() ) );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import manifold.json.rt.api.*;
import manifold.rt.api.*;
import manifold.rt.api.util.ManClassUtil;
import manifold.rt.api.util.ManEscapeUtil;
import manifold.rt.api.util.Pair;
import manifold.sql.api.DataElement;
import manifold.sql.query.api.ForeignKeyQueryRef;
Expand Down Expand Up @@ -92,12 +93,12 @@ private void addRunMethods( SrcLinkedClass srcClass )
{
//addRunMethod( srcClass, "runFlat", "FlatRow" );

Pair<SchemaTable, List<QueryColumn>> primaryTable = getQuery().findPrimaryTable();
Pair<SchemaTable, List<QueryColumn>> selectedTable = getQuery().findSelectedTable();
String rowType;
if( primaryTable != null && primaryTable.getSecond().size() == getQuery().getColumns().size() )
if( selectedTable != null && selectedTable.getSecond().size() == getQuery().getColumns().size() )
{
// Single table query can be represented directly as TableResult e.g., SELECT * queries.
SchemaTable table = primaryTable.getFirst();
SchemaTable table = selectedTable.getFirst();
rowType = getTableFqn( table );
}
else
Expand Down Expand Up @@ -181,6 +182,7 @@ private void addRunMethod( SrcLinkedClass srcClass, String methodName, @Suppress

//noinspection unused
String query = getQuery() == null ? "errant query" : getQuery().getQuerySource();
query = ManEscapeUtil.escapeForJavaStringLiteral( query );
//noinspection unused
String configName = _model.getScope().getDbconfig().getName();
//noinspection unused
Expand Down Expand Up @@ -275,13 +277,13 @@ private void addRowType( SrcLinkedClass enclosingType )
{
Map<String, QueryColumn> columns = getQuery().getColumns();

Pair<SchemaTable, List<QueryColumn>> primaryTable = getQuery().findPrimaryTable();
if( primaryTable != null )
Pair<SchemaTable, List<QueryColumn>> selectedTable = getQuery().findSelectedTable();
if( selectedTable != null )
{
addPrimaryAccessor( srcClass, primaryTable );
addSelectedTableAccessor( srcClass, selectedTable );

List<QueryColumn> primaryCols = primaryTable.getSecond();
primaryCols.forEach( c -> columns.remove( c.getName() ) );
List<QueryColumn> selectedCols = selectedTable.getSecond();
selectedCols.forEach( c -> columns.remove( c.getName() ) );
}

for( ForeignKeyQueryRef fkRef : getQuery().findForeignKeyQueryRefs() )
Expand Down Expand Up @@ -342,17 +344,20 @@ private void addFkFetcher( SrcLinkedClass srcClass, ForeignKeyQueryRef fkRef )
srcClass.addMethod( fkFetchMethod );
}

private void addPrimaryAccessor( SrcLinkedClass srcClass, Pair<SchemaTable, List<QueryColumn>> primaryTable )
private void addSelectedTableAccessor( SrcLinkedClass srcClass, Pair<SchemaTable, List<QueryColumn>> selectedTable )
{
SchemaTable table = primaryTable.getFirst();
SchemaTable table = selectedTable.getFirst();
String tableName = getTableSimpleTypeName( table );
String tableFqn = getTableFqn( table );

String propName = tableName;
if( getQuery().getColumns().keySet().stream()
.map( n -> makePascalCaseIdentifier( n, true ) ).anyMatch( n -> n.equals( tableName ) ) )
boolean tableNameMatchesColumnNameOfRemainingColumns = getQuery().getColumns().values().stream()
.filter( c -> !selectedTable.getSecond().contains( c ) )
.map( c -> makePascalCaseIdentifier( c.getName(), true ) )
.anyMatch( name -> name.equalsIgnoreCase( tableName ) );
if( tableNameMatchesColumnNameOfRemainingColumns )
{
// disambiguate primary table obj and query column
// disambiguate selected table obj and query column
propName += "Ref";
}
SrcType type = new SrcType( tableFqn );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package manifold.sql.schema.jdbc;

import manifold.sql.schema.api.SchemaColumn;
import manifold.util.ReflectUtil;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;

public class JdbcSchemaColumn implements SchemaColumn
Expand All @@ -45,6 +47,17 @@ public JdbcSchemaColumn( int colIndex, JdbcSchemaTable jdbcSchemaTable, ResultSe
_table = jdbcSchemaTable;
_name = rs.getString( "COLUMN_NAME" );
_jdbcType = rs.getInt( "DATA_TYPE" );
// try
// {
// // this value is the SQL type name like VARCHAR(50) etc., although sometimes it aligns with JDBC types like TIMESTAMP
// String typeName = rs.getString( "TYPE_NAME" );
// if( typeName != null )
// {
// _jdbcType = (int)ReflectUtil.field( Types.class, typeName ).getStatic();
// }
// }
// catch( Exception ignore )
// {}
_isNullable = rs.getInt( "NULLABLE" ) == DatabaseMetaData.columnNullable;
_isGenerated = "YES".equals( rs.getString( "IS_GENERATEDCOLUMN" ) );
_isPrimaryKeyPart = primaryKey.contains( _name );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public JdbcSchemaTable( JdbcSchema owner, DatabaseMetaData metaData, ResultSet r

_columns = new LinkedHashMap<>();
_primaryKeys = new ArrayList<>();
try( ResultSet colResults = metaData.getColumns( null, null, _name, null ) )
try( ResultSet colResults = metaData.getColumns( catalogName, schemaName, _name, null ) )
{
int i = 0;
JdbcSchemaColumn id = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,17 @@ private void addMember( SrcLinkedClass srcInterface, SchemaColumn member, Class<
// StringBuilder componentType = getComponentType( getterType ).render( new StringBuilder(), 0, false );
SrcGetProperty getter = new SrcGetProperty( propName, getterType );
getter.modifiers( Flags.DEFAULT );
getter.body( "return (${getterType.getFqName()})getBindings().get(\"$colName\");" );
if( type == String.class )
{
// handling string specially because retarded SQLite reports table columns are VARCHAR for stuff like TIMESTAMP,
// but query results are reported to have the actual type TIMESTAMP, so must coerce to string here :(
getter.body( "Object value = getBindings().get(\"$colName\");" +
"return value == null ? null : String.valueOf(value);" );
}
else
{
getter.body( "return (${getterType.getFqName()})getBindings().get(\"$colName\");" );
}
addActualNameAnnotation( getter, name, true );
srcInterface.addGetProperty( getter ).modifiers( Modifier.PUBLIC );

Expand Down

0 comments on commit cf41e4d

Please sign in to comment.