Much implementation of joins for RDBMS

This commit is contained in:
2022-11-23 16:37:54 -06:00
parent 6685e61500
commit b2d76e8206
26 changed files with 1873 additions and 104 deletions

View File

@ -27,7 +27,9 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
@ -36,12 +38,17 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -168,9 +175,88 @@ public abstract class AbstractRDBMSAction implements QActionInterface
/*******************************************************************************
**
*******************************************************************************/
protected String makeWhereClause(QTableMetaData table, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException
protected String makeFromClause(QInstance instance, String tableName, JoinsContext joinsContext) throws QException
{
String clause = makeWhereClause(table, filter.getCriteria(), filter.getBooleanOperator(), params);
StringBuilder rs = new StringBuilder(escapeIdentifier(getTableName(instance.getTable(tableName))) + " AS " + escapeIdentifier(tableName));
for(QueryJoin queryJoin : joinsContext.getQueryJoins())
{
QTableMetaData joinTable = instance.getTable(queryJoin.getRightTable());
String tableNameOrAlias = queryJoin.getAliasOrRightTable();
rs.append(" ").append(queryJoin.getType()).append(" JOIN ")
.append(escapeIdentifier(getTableName(joinTable)))
.append(" AS ").append(escapeIdentifier(tableNameOrAlias));
////////////////////////////////////////////////////////////
// find the join in the instance, to see the 'on' clause //
////////////////////////////////////////////////////////////
List<String> joinClauseList = new ArrayList<>();
String leftTableName = joinsContext.resolveTableNameOrAliasToTableName(queryJoin.getLeftTableOrAlias());
QJoinMetaData joinMetaData = Objects.requireNonNullElseGet(queryJoin.getJoinMetaData(), () -> findJoinMetaData(instance, leftTableName, queryJoin.getRightTable()));
for(JoinOn joinOn : joinMetaData.getJoinOns())
{
QTableMetaData leftTable = instance.getTable(joinMetaData.getLeftTable());
QTableMetaData rightTable = instance.getTable(joinMetaData.getRightTable());
String leftTableOrAlias = queryJoin.getLeftTableOrAlias();
String aliasOrRightTable = queryJoin.getAliasOrRightTable();
joinClauseList.add(escapeIdentifier(leftTableOrAlias)
+ "." + escapeIdentifier(getColumnName(leftTable.getField(joinOn.getLeftField())))
+ " = " + escapeIdentifier(aliasOrRightTable)
+ "." + escapeIdentifier(getColumnName((rightTable.getField(joinOn.getRightField())))));
}
rs.append(" ON ").append(StringUtils.join(" AND ", joinClauseList));
}
return (rs.toString());
}
/*******************************************************************************
**
*******************************************************************************/
private QJoinMetaData findJoinMetaData(QInstance instance, String leftTable, String rightTable)
{
List<QJoinMetaData> matches = new ArrayList<>();
for(QJoinMetaData join : instance.getJoins().values())
{
if(join.getLeftTable().equals(leftTable) && join.getRightTable().equals(rightTable))
{
matches.add(join);
}
//////////////////////////////
// look in both directions! //
//////////////////////////////
if(join.getRightTable().equals(leftTable) && join.getLeftTable().equals(rightTable))
{
matches.add(join.flip());
}
}
if(matches.size() == 1)
{
return (matches.get(0));
}
else if(matches.size() > 1)
{
throw (new RuntimeException("More than 1 join was found between [" + leftTable + "] and [" + rightTable + "]. Specify which one in your QueryJoin."));
}
return (null);
}
/*******************************************************************************
**
*******************************************************************************/
protected String makeWhereClause(QInstance instance, QTableMetaData table, JoinsContext joinsContext, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException, QException
{
String clause = makeSimpleWhereClause(instance, table, joinsContext, filter.getCriteria(), filter.getBooleanOperator(), params);
if(!CollectionUtils.nullSafeHasContents(filter.getSubFilters()))
{
///////////////////////////////////////////////////////////////
@ -189,7 +275,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
}
for(QQueryFilter subFilter : filter.getSubFilters())
{
String subClause = makeWhereClause(table, subFilter, params);
String subClause = makeWhereClause(instance, table, joinsContext, subFilter, params);
if(StringUtils.hasContent(subClause))
{
clauses.add("(" + subClause + ")");
@ -203,14 +289,16 @@ public abstract class AbstractRDBMSAction implements QActionInterface
/*******************************************************************************
**
*******************************************************************************/
private String makeWhereClause(QTableMetaData table, List<QFilterCriteria> criteria, QQueryFilter.BooleanOperator booleanOperator, List<Serializable> params) throws IllegalArgumentException
private String makeSimpleWhereClause(QInstance instance, QTableMetaData table, JoinsContext joinsContext, List<QFilterCriteria> criteria, QQueryFilter.BooleanOperator booleanOperator, List<Serializable> params) throws IllegalArgumentException
{
List<String> clauses = new ArrayList<>();
for(QFilterCriteria criterion : criteria)
{
QFieldMetaData field = table.getField(criterion.getFieldName());
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(criterion.getFieldName());
List<Serializable> values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues());
String column = getColumnName(field);
QFieldMetaData field = fieldAndTableNameOrAlias.field();
String column = escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(field));
String clause = column;
Integer expectedNoOfParams = null;
switch(criterion.getOperator())
@ -360,15 +448,29 @@ public abstract class AbstractRDBMSAction implements QActionInterface
throw new IllegalArgumentException("Unexpected operator: " + criterion.getOperator());
}
}
clauses.add("(" + clause + ")");
if(expectedNoOfParams != null)
{
if(!expectedNoOfParams.equals(values.size()))
if(expectedNoOfParams.equals(1) && StringUtils.hasContent(criterion.getOtherFieldName()))
{
JoinsContext.FieldAndTableNameOrAlias otherFieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(criterion.getOtherFieldName());
String otherColumn = escapeIdentifier(otherFieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(otherFieldAndTableNameOrAlias.field()));
clause = clause.replace("?", otherColumn);
/////////////////////////////////////////////////////////////////////
// make sure we don't add any values in this case, just in case... //
/////////////////////////////////////////////////////////////////////
values = Collections.emptyList();
}
else if(!expectedNoOfParams.equals(values.size()))
{
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]");
}
}
clauses.add("(" + clause + ")");
params.addAll(values);
}
@ -470,7 +572,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
/*******************************************************************************
**
*******************************************************************************/
protected String makeOrderByClause(QTableMetaData table, List<QFilterOrderBy> orderBys)
protected String makeOrderByClause(QTableMetaData table, List<QFilterOrderBy> orderBys, JoinsContext joinsContext)
{
List<String> clauses = new ArrayList<>();
@ -485,9 +587,11 @@ public abstract class AbstractRDBMSAction implements QActionInterface
}
else
{
QFieldMetaData field = table.getField(orderBy.getFieldName());
String column = escapeIdentifier(getColumnName(field));
clauses.add(column + " " + ascOrDesc);
JoinsContext.FieldAndTableNameOrAlias otherFieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(orderBy.getFieldName());
QFieldMetaData field = otherFieldAndTableNameOrAlias.field();
String column = getColumnName(field);
clauses.add(escapeIdentifier(otherFieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(column) + " " + ascOrDesc);
}
}
return (String.join(", ", clauses));

View File

@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateIn
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
@ -61,29 +62,30 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
{
try
{
QTableMetaData table = aggregateInput.getTable();
String tableName = getTableName(table);
QTableMetaData table = aggregateInput.getTable();
List<String> selectClauses = buildSelectClauses(aggregateInput);
JoinsContext joinsContext = new JoinsContext(aggregateInput.getInstance(), table.getName(), aggregateInput.getQueryJoins());
String fromClause = makeFromClause(aggregateInput.getInstance(), table.getName(), joinsContext);
List<String> selectClauses = buildSelectClauses(aggregateInput, joinsContext);
String sql = "SELECT " + StringUtils.join(", ", selectClauses)
+ " FROM " + escapeIdentifier(tableName);
+ " FROM " + fromClause;
QQueryFilter filter = aggregateInput.getFilter();
List<Serializable> params = new ArrayList<>();
if(filter != null && filter.hasAnyCriteria())
{
sql += " WHERE " + makeWhereClause(table, filter, params);
sql += " WHERE " + makeWhereClause(aggregateInput.getInstance(), table, joinsContext, filter, params);
}
if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupByFieldNames()))
{
sql += " GROUP BY " + makeGroupByClause(aggregateInput);
sql += " GROUP BY " + makeGroupByClause(aggregateInput, joinsContext);
}
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getOrderBys()))
{
sql += " ORDER BY " + makeOrderByClause(table, filter.getOrderBys());
sql += " ORDER BY " + makeOrderByClause(table, filter.getOrderBys(), joinsContext);
}
// todo sql customization - can edit sql and/or param list
@ -105,13 +107,16 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
int selectionIndex = 1;
for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames()))
{
Serializable value = getFieldValueFromResultSet(table.getField(groupByFieldName), resultSet, selectionIndex++);
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
Serializable value = getFieldValueFromResultSet(fieldAndTableNameOrAlias.field(), resultSet, selectionIndex++);
result.withGroupByValue(groupByFieldName, value);
}
for(Aggregate aggregate : aggregateInput.getAggregates())
{
QFieldMetaData field = table.getField(aggregate.getFieldName());
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(aggregate.getFieldName());
QFieldMetaData field = fieldAndTableNameOrAlias.field();
if(field.getType().equals(QFieldType.INTEGER) && aggregate.getOperator().equals(AggregateOperator.AVG))
{
field = new QFieldMetaData().withType(QFieldType.DECIMAL);
@ -139,19 +144,20 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
/*******************************************************************************
**
*******************************************************************************/
private List<String> buildSelectClauses(AggregateInput aggregateInput)
private List<String> buildSelectClauses(AggregateInput aggregateInput, JoinsContext joinsContext)
{
QTableMetaData table = aggregateInput.getTable();
List<String> rs = new ArrayList<>();
List<String> rs = new ArrayList<>();
for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames()))
{
rs.add(escapeIdentifier(getColumnName(table.getField(groupByFieldName))));
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
rs.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())));
}
for(Aggregate aggregate : aggregateInput.getAggregates())
{
rs.add(aggregate.getOperator() + "(" + escapeIdentifier(getColumnName(table.getField(aggregate.getFieldName()))) + ")");
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(aggregate.getFieldName());
rs.add(aggregate.getOperator() + "(" + escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())) + ")");
}
return (rs);
}
@ -161,13 +167,13 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
/*******************************************************************************
**
*******************************************************************************/
private String makeGroupByClause(AggregateInput aggregateInput)
private String makeGroupByClause(AggregateInput aggregateInput, JoinsContext joinsContext)
{
QTableMetaData table = aggregateInput.getTable();
List<String> columns = new ArrayList<>();
List<String> columns = new ArrayList<>();
for(String groupByFieldName : aggregateInput.getGroupByFieldNames())
{
columns.add(escapeIdentifier(getColumnName(table.getField(groupByFieldName))));
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
columns.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())));
}
return (StringUtils.join(",", columns));

View File

@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
@ -54,16 +55,18 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
{
try
{
QTableMetaData table = countInput.getTable();
String tableName = getTableName(table);
QTableMetaData table = countInput.getTable();
String sql = "SELECT count(*) as record_count FROM " + escapeIdentifier(tableName);
JoinsContext joinsContext = new JoinsContext(countInput.getInstance(), countInput.getTableName(), countInput.getQueryJoins());
String sql = "SELECT count(*) as record_count FROM "
+ makeFromClause(countInput.getInstance(), table.getName(), joinsContext);
QQueryFilter filter = countInput.getFilter();
List<Serializable> params = new ArrayList<>();
if(filter != null && filter.hasAnyCriteria())
{
sql += " WHERE " + makeWhereClause(table, filter, params);
sql += " WHERE " + makeWhereClause(countInput.getInstance(), table, joinsContext, filter, params);
}
// todo sql customization - can edit sql and/or param list

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.Serializable;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
@ -32,6 +33,7 @@ import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -258,8 +260,9 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
List<Serializable> params = new ArrayList<>();
QTableMetaData table = deleteInput.getTable();
String tableName = getTableName(table);
String whereClause = makeWhereClause(table, filter, params);
String tableName = getTableName(table);
JoinsContext joinsContext = new JoinsContext(deleteInput.getInstance(), table.getName(), Collections.emptyList());
String whereClause = makeWhereClause(deleteInput.getInstance(), table, joinsContext, filter, params);
// todo sql customization - can edit sql and/or param list?
String sql = "DELETE FROM "

View File

@ -34,10 +34,13 @@ import java.util.List;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -64,35 +67,34 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
try
{
QTableMetaData table = queryInput.getTable();
String tableName = getTableName(table);
String tableName = queryInput.getTableName();
List<QFieldMetaData> fieldList = new ArrayList<>(table.getFields().values());
String columns = fieldList.stream()
.map(this::getColumnName)
.collect(Collectors.joining(", "));
StringBuilder sql = new StringBuilder("SELECT ").append(makeSelectClause(queryInput.getInstance(), tableName, queryInput.getQueryJoins()));
String sql = "SELECT " + columns + " FROM " + escapeIdentifier(tableName);
JoinsContext joinsContext = new JoinsContext(queryInput.getInstance(), tableName, queryInput.getQueryJoins());
sql.append(" FROM ").append(makeFromClause(queryInput.getInstance(), tableName, joinsContext));
QQueryFilter filter = queryInput.getFilter();
List<Serializable> params = new ArrayList<>();
if(filter != null && filter.hasAnyCriteria())
{
sql += " WHERE " + makeWhereClause(table, filter, params);
sql.append(" WHERE ").append(makeWhereClause(queryInput.getInstance(), table, joinsContext, filter, params));
}
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getOrderBys()))
{
sql += " ORDER BY " + makeOrderByClause(table, filter.getOrderBys());
sql.append(" ORDER BY ").append(makeOrderByClause(table, filter.getOrderBys(), joinsContext));
}
if(queryInput.getLimit() != null)
{
sql += " LIMIT " + queryInput.getLimit();
sql.append(" LIMIT ").append(queryInput.getLimit());
if(queryInput.getSkip() != null)
{
// todo - other sql grammars?
sql += " OFFSET " + queryInput.getSkip();
sql.append(" OFFSET ").append(queryInput.getSkip());
}
}
@ -111,10 +113,31 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
needToCloseConnection = true;
}
////////////////////////////////////////////////////////////////////////////
// build the list of fields that will be processed in the result-set loop //
////////////////////////////////////////////////////////////////////////////
List<QFieldMetaData> fieldList = new ArrayList<>(table.getFields().values());
for(QueryJoin queryJoin : CollectionUtils.nonNullList(queryInput.getQueryJoins()))
{
if(queryJoin.getSelect())
{
QTableMetaData joinTable = queryInput.getInstance().getTable(queryJoin.getRightTable());
String tableNameOrAlias = queryJoin.getAliasOrRightTable();
for(QFieldMetaData joinField : joinTable.getFields().values())
{
fieldList.add(joinField.clone().withName(tableNameOrAlias + "." + joinField.getName()));
}
}
}
try
{
QueryOutput queryOutput = new QueryOutput(queryInput);
PreparedStatement statement = createStatement(connection, sql, queryInput);
//////////////////////////////////////////////
// execute the query - iterate over results //
//////////////////////////////////////////////
QueryOutput queryOutput = new QueryOutput(queryInput);
System.out.println(sql);
PreparedStatement statement = createStatement(connection, sql.toString(), queryInput);
QueryManager.executeStatement(statement, ((ResultSet resultSet) ->
{
ResultSetMetaData metaData = resultSet.getMetaData();
@ -162,6 +185,42 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
/*******************************************************************************
**
*******************************************************************************/
private String makeSelectClause(QInstance instance, String tableName, List<QueryJoin> queryJoins) throws QException
{
QTableMetaData table = instance.getTable(tableName);
List<QFieldMetaData> fieldList = new ArrayList<>(table.getFields().values());
String columns = fieldList.stream()
.map(field -> escapeIdentifier(tableName) + "." + escapeIdentifier(getColumnName(field)))
.collect(Collectors.joining(", "));
StringBuilder rs = new StringBuilder(columns);
for(QueryJoin queryJoin : CollectionUtils.nonNullList(queryJoins))
{
if(queryJoin.getSelect())
{
QTableMetaData joinTable = instance.getTable(queryJoin.getRightTable());
String tableNameOrAlias = queryJoin.getAliasOrRightTable();
if(joinTable == null)
{
throw new QException("Requested join table [" + queryJoin.getRightTable() + "] is not a defined table.");
}
List<QFieldMetaData> joinFieldList = new ArrayList<>(joinTable.getFields().values());
String joinColumns = joinFieldList.stream()
.map(field -> escapeIdentifier(tableNameOrAlias) + "." + escapeIdentifier(getColumnName(field)))
.collect(Collectors.joining(", "));
rs.append(", ").append(joinColumns);
}
}
return (rs.toString());
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -29,6 +29,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSActionTest;
@ -45,9 +48,15 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
*******************************************************************************/
public class TestUtils
{
public static final String DEFAULT_BACKEND_NAME = "default";
public static final String TABLE_NAME_PERSON = "personTable";
public static final String TABLE_NAME_PERSONAL_ID_CARD = "personalIdCard";
public static final String TABLE_NAME_STORE = "store";
public static final String TABLE_NAME_ORDER = "order";
public static final String TABLE_NAME_ITEM = "item";
public static final String TABLE_NAME_ORDER_LINE = "orderLine";
/*******************************************************************************
@ -81,6 +90,9 @@ public class TestUtils
QInstance qInstance = new QInstance();
qInstance.addBackend(defineBackend());
qInstance.addTable(defineTablePerson());
qInstance.addTable(defineTablePersonalIdCard());
qInstance.addJoin(defineJoinPersonAndPersonalIdCard());
addOmsTablesAndJoins(qInstance);
qInstance.setAuthentication(defineAuthentication());
return (qInstance);
}
@ -121,9 +133,9 @@ public class TestUtils
public static QTableMetaData defineTablePerson()
{
return new QTableMetaData()
.withName("a-person") // use this name, so it isn't the same as the actual database-table name (which must come from the backend details)
.withName(TABLE_NAME_PERSON)
.withLabel("Person")
.withBackendName(defineBackend().getName())
.withBackendName(DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date"))
@ -139,4 +151,136 @@ public class TestUtils
.withTableName("person"));
}
/*******************************************************************************
** Define a 1:1 table with Person.
**
*******************************************************************************/
private static QTableMetaData defineTablePersonalIdCard()
{
return new QTableMetaData()
.withName(TABLE_NAME_PERSONAL_ID_CARD)
.withLabel("Personal Id Card")
.withBackendName(DEFAULT_BACKEND_NAME)
.withBackendDetails(new RDBMSTableBackendDetails()
.withTableName("personal_id_card"))
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date"))
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date"))
.withField(new QFieldMetaData("personId", QFieldType.INTEGER).withBackendName("person_id"))
.withField(new QFieldMetaData("idNumber", QFieldType.STRING).withBackendName("id_number"));
}
/*******************************************************************************
**
*******************************************************************************/
private static QJoinMetaData defineJoinPersonAndPersonalIdCard()
{
return new QJoinMetaData()
.withLeftTable(TABLE_NAME_PERSON)
.withRightTable(TABLE_NAME_PERSONAL_ID_CARD)
.withInferredName()
.withType(JoinType.ONE_TO_ONE)
.withJoinOn(new JoinOn("id", "personId"));
}
/*******************************************************************************
**
*******************************************************************************/
private static void addOmsTablesAndJoins(QInstance qInstance)
{
qInstance.addTable(defineBaseTable(TABLE_NAME_STORE, "store")
.withField(new QFieldMetaData("name", QFieldType.STRING))
);
qInstance.addTable(defineBaseTable(TABLE_NAME_ORDER, "order")
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id"))
.withField(new QFieldMetaData("billToPersonId", QFieldType.INTEGER).withBackendName("bill_to_person_id"))
.withField(new QFieldMetaData("shipToPersonId", QFieldType.INTEGER).withBackendName("ship_to_person_id"))
);
qInstance.addTable(defineBaseTable(TABLE_NAME_ITEM, "item")
.withField(new QFieldMetaData("sku", QFieldType.STRING))
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id"))
);
qInstance.addTable(defineBaseTable(TABLE_NAME_ORDER_LINE, "order_line")
.withField(new QFieldMetaData("orderId", QFieldType.INTEGER).withBackendName("order_id"))
.withField(new QFieldMetaData("sku", QFieldType.STRING))
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id"))
.withField(new QFieldMetaData("quantity", QFieldType.INTEGER))
);
qInstance.addJoin(new QJoinMetaData()
.withName("orderJoinStore")
.withLeftTable(TABLE_NAME_ORDER)
.withRightTable(TABLE_NAME_STORE)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("storeId", "id"))
);
qInstance.addJoin(new QJoinMetaData()
.withName("orderJoinBillToPerson")
.withLeftTable(TABLE_NAME_ORDER)
.withRightTable(TABLE_NAME_PERSON)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("billToPersonId", "id"))
);
qInstance.addJoin(new QJoinMetaData()
.withName("orderJoinShipToPerson")
.withLeftTable(TABLE_NAME_ORDER)
.withRightTable(TABLE_NAME_PERSON)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("shipToPersonId", "id"))
);
qInstance.addJoin(new QJoinMetaData()
.withName("itemJoinStore")
.withLeftTable(TABLE_NAME_ITEM)
.withRightTable(TABLE_NAME_STORE)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("storeId", "id"))
);
qInstance.addJoin(new QJoinMetaData()
.withName("orderJoinOrderLine")
.withLeftTable(TABLE_NAME_ORDER)
.withRightTable(TABLE_NAME_ORDER_LINE)
.withType(JoinType.ONE_TO_MANY)
.withJoinOn(new JoinOn("id", "orderId"))
);
qInstance.addJoin(new QJoinMetaData()
.withName("orderLineJoinItem")
.withLeftTable(TABLE_NAME_ORDER_LINE)
.withRightTable(TABLE_NAME_ITEM)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("sku", "sku"))
.withJoinOn(new JoinOn("storeId", "storeId"))
);
}
/*******************************************************************************
**
*******************************************************************************/
private static QTableMetaData defineBaseTable(String tableName, String backendTableName)
{
return new QTableMetaData()
.withName(tableName)
.withBackendName(DEFAULT_BACKEND_NAME)
.withBackendDetails(new RDBMSTableBackendDetails().withTableName(backendTableName))
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER));
}
}

View File

@ -38,13 +38,16 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperat
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
@ -294,6 +297,71 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsJoinAggregate() throws Exception
{
AggregateInput aggregateInput = new AggregateInput(TestUtils.defineInstance());
Aggregate sumOfQuantity = new Aggregate(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity", AggregateOperator.SUM);
aggregateInput.setSession(new QSession());
aggregateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
aggregateInput.withAggregate(sumOfQuantity);
aggregateInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE));
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
AggregateResult aggregateResult = aggregateOutput.getResults().get(0);
Assertions.assertEquals(43, aggregateResult.getAggregateValue(sumOfQuantity));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsJoinGroupBy() throws Exception
{
AggregateInput aggregateInput = new AggregateInput(TestUtils.defineInstance());
Aggregate sumOfQuantity = new Aggregate(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity", AggregateOperator.SUM);
aggregateInput.setSession(new QSession());
aggregateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
aggregateInput.withAggregate(sumOfQuantity);
aggregateInput.withGroupByFieldName(TestUtils.TABLE_NAME_ORDER_LINE + ".sku");
aggregateInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE));
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
assertEquals(6, aggregateOutput.getResults().size());
assertSkuQuantity("QM-1", 30, aggregateOutput.getResults());
assertSkuQuantity("QM-2", 1, aggregateOutput.getResults());
assertSkuQuantity("QM-3", 1, aggregateOutput.getResults());
assertSkuQuantity("QRU-1", 3, aggregateOutput.getResults());
assertSkuQuantity("QRU-2", 2, aggregateOutput.getResults());
assertSkuQuantity("QD-1", 6, aggregateOutput.getResults());
}
/*******************************************************************************
**
*******************************************************************************/
private void assertSkuQuantity(String sku, int quantity, List<AggregateResult> results)
{
for(AggregateResult result : results)
{
if(result.getGroupByValue("orderLine.sku").equals(sku))
{
assertEquals(quantity, result.getAggregateValues().values().iterator().next());
return;
}
}
fail("Didn't find SKU " + sku + " in aggregate results");
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -23,16 +23,19 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
@ -60,7 +63,7 @@ public class RDBMSCountActionTest extends RDBMSActionTest
{
CountInput countInput = initCountRequest();
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(5, countOutput.getCount(), "Unfiltered query should find all rows");
assertEquals(5, countOutput.getCount(), "Unfiltered query should find all rows");
}
@ -81,7 +84,7 @@ public class RDBMSCountActionTest extends RDBMSActionTest
.withValues(List.of(email)))
);
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(1, countOutput.getCount(), "Expected # of rows");
assertEquals(1, countOutput.getCount(), "Expected # of rows");
}
@ -102,7 +105,7 @@ public class RDBMSCountActionTest extends RDBMSActionTest
.withValues(List.of(email)))
);
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(4, countOutput.getCount(), "Expected # of rows");
assertEquals(4, countOutput.getCount(), "Expected # of rows");
}
@ -114,8 +117,66 @@ public class RDBMSCountActionTest extends RDBMSActionTest
{
CountInput countInput = new CountInput();
countInput.setInstance(TestUtils.defineInstance());
countInput.setSession(new QSession());
countInput.setTableName(TestUtils.defineTablePerson().getName());
return countInput;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithoutWhere() throws QException
{
CountInput countInput = initCountRequest();
countInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD));
CountOutput countOutput = new CountAction().execute(countInput);
assertEquals(3, countOutput.getCount(), "Join count should find 3 rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneLeftJoinWithoutWhere() throws QException
{
CountInput countInput = initCountRequest();
countInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withType(QueryJoin.Type.LEFT));
CountOutput countOutput = new CountAction().execute(countInput);
assertEquals(5, countOutput.getCount(), "Left Join count should find 5 rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneRightJoinWithoutWhere() throws QException
{
CountInput countInput = initCountRequest();
countInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withType(QueryJoin.Type.RIGHT));
CountOutput countOutput = new CountAction().execute(countInput);
assertEquals(6, countOutput.getCount(), "Right Join count should find 6 rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithWhere() throws QException
{
CountInput countInput = initCountRequest();
countInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withSelect(true));
countInput.setFilter(new QQueryFilter(new QFilterCriteria(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber", QCriteriaOperator.STARTS_WITH, "1980")));
CountOutput countOutput = new CountAction().execute(countInput);
assertEquals(2, countOutput.getCount(), "Right Join count should find 2 rows");
}
}

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
@ -30,16 +31,21 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
@ -67,7 +73,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
{
QueryInput queryInput = initQueryRequest();
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
}
@ -88,8 +94,8 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(email)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertEquals(email, queryOutput.getRecords().get(0).getValueString("email"), "Should find expected email address");
assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(email, queryOutput.getRecords().get(0).getValueString("email"), "Should find expected email address");
}
@ -110,7 +116,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(email)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").equals(email)), "Should NOT find expected email address");
}
@ -130,7 +136,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(2, 4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(4)), "Should find expected ids");
}
@ -150,7 +156,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(2, 3, 4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
@ -170,7 +176,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("darin")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
@ -190,7 +196,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("kelkhoff")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
@ -210,7 +216,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("gmail.com")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
@ -230,7 +236,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("darin")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
@ -250,7 +256,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("kelkhoff")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
@ -270,7 +276,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of("gmail.com")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
@ -290,7 +296,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(3)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
@ -310,7 +316,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(2)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
@ -330,7 +336,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(3)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
@ -350,7 +356,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
@ -369,7 +375,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withOperator(QCriteriaOperator.IS_BLANK)
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("birthDate") == null), "Should find expected row");
}
@ -388,7 +394,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withOperator(QCriteriaOperator.IS_NOT_BLANK)
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(5, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("firstName") != null), "Should find expected rows");
}
@ -408,7 +414,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(2, 4))
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(3, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(3, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(3) || r.getValueInteger("id").equals(4)), "Should find expected ids");
}
@ -428,7 +434,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(2, 4))
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
@ -441,7 +447,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
{
QueryInput queryInput = new QueryInput();
queryInput.setInstance(TestUtils.defineInstance());
queryInput.setTableName(TestUtils.defineTablePerson().getName());
queryInput.setTableName(TestUtils.TABLE_NAME_PERSON);
queryInput.setSession(new QSession());
return queryInput;
}
@ -459,7 +465,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
QueryInput queryInput = initQueryRequest();
queryInput.setShouldGenerateDisplayValues(true);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
for(QRecord record : queryOutput.getRecords())
{
@ -479,7 +485,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName(TestUtils.defineTablePerson().getName());
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON);
InsertAction insertAction = new InsertAction();
QBackendTransaction transaction = insertAction.openTransaction(insertInput);
@ -493,12 +499,12 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
QueryInput queryInput = initQueryRequest();
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Query without the transaction should not see the new row.");
assertEquals(5, queryOutput.getRecords().size(), "Query without the transaction should not see the new row.");
queryInput = initQueryRequest();
queryInput.setTransaction(transaction);
queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(6, queryOutput.getRecords().size(), "Query with the transaction should see the new row.");
assertEquals(6, queryOutput.getRecords().size(), "Query with the transaction should see the new row.");
transaction.rollback();
}
@ -514,11 +520,11 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.IN, List.of())));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(0, queryOutput.getRecords().size(), "IN empty list should find nothing.");
assertEquals(0, queryOutput.getRecords().size(), "IN empty list should find nothing.");
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.NOT_IN, List.of())));
queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "NOT_IN empty list should find everything.");
assertEquals(5, queryOutput.getRecords().size(), "NOT_IN empty list should find everything.");
}
@ -536,7 +542,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Tim")))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "OR should find 2 rows");
assertEquals(2, queryOutput.getRecords().size(), "OR should find 2 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim"));
}
@ -564,7 +570,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Complex query should find 2 rows");
assertEquals(2, queryOutput.getRecords().size(), "Complex query should find 2 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("lastName").equals("Maes"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("lastName").equals("Kelkhoff"));
}
@ -592,8 +598,322 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Complex query should find 1 row");
assertEquals(1, queryOutput.getRecords().size(), "Complex query should find 1 row");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim") && r.getValueString("lastName").equals("Chamberlain"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithoutWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withSelect(true));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(3, queryOutput.getRecords().size(), "Join query should find 3 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("personalIdCard.idNumber").equals("19800531"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("personalIdCard.idNumber").equals("19800515"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim") && r.getValueString("personalIdCard.idNumber").equals("19760528"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneLeftJoinWithoutWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withType(QueryJoin.Type.LEFT).withSelect(true));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(5, queryOutput.getRecords().size(), "Left Join query should find 5 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("personalIdCard.idNumber").equals("19800531"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("personalIdCard.idNumber").equals("19800515"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim") && r.getValueString("personalIdCard.idNumber").equals("19760528"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Garret") && r.getValue("personalIdCard.idNumber") == null);
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tyler") && r.getValue("personalIdCard.idNumber") == null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneRightJoinWithoutWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withType(QueryJoin.Type.RIGHT).withSelect(true));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(6, queryOutput.getRecords().size(), "Right Join query should find 6 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("personalIdCard.idNumber").equals("19800531"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("personalIdCard.idNumber").equals("19800515"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim") && r.getValueString("personalIdCard.idNumber").equals("19760528"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValue("firstName") == null && r.getValueString("personalIdCard.idNumber").equals("123123123"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValue("firstName") == null && r.getValueString("personalIdCard.idNumber").equals("987987987"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValue("firstName") == null && r.getValueString("personalIdCard.idNumber").equals("456456456"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_PERSON, TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withSelect(true));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber", QCriteriaOperator.STARTS_WITH, "1980")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(2, queryOutput.getRecords().size(), "Join query should find 2 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("personalIdCard.idNumber").equals("19800531"));
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("personalIdCard.idNumber").equals("19800515"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithOrderBy() throws QException
{
QInstance qInstance = TestUtils.defineInstance();
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(qInstance.getJoin(TestUtils.TABLE_NAME_PERSON + "Join" + TestUtils.TABLE_NAME_PERSONAL_ID_CARD)).withSelect(true));
queryInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(3, queryOutput.getRecords().size(), "Join query should find 3 rows");
List<String> idNumberListFromQuery = queryOutput.getRecords().stream().map(r -> r.getValueString(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber")).toList();
assertEquals(List.of("19760528", "19800515", "19800531"), idNumberListFromQuery);
/////////////////////////
// repeat, sorted desc //
/////////////////////////
queryInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber", false)));
queryOutput = new QueryAction().execute(queryInput);
assertEquals(3, queryOutput.getRecords().size(), "Join query should find 3 rows");
idNumberListFromQuery = queryOutput.getRecords().stream().map(r -> r.getValueString(TestUtils.TABLE_NAME_PERSONAL_ID_CARD + ".idNumber")).toList();
assertEquals(List.of("19800531", "19800515", "19760528"), idNumberListFromQuery);
}
/*******************************************************************************
** In the prime data, we've got 1 order line set up with an item from a different
** store than its order. Write a query to find such a case.
*******************************************************************************/
@Test
void testFiveTableOmsJoinFindMismatchedStoreId() throws Exception
{
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession());
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_STORE).withAlias("orderStore").withSelect(true));
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE).withSelect(true));
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_LINE, TestUtils.TABLE_NAME_ITEM).withSelect(true));
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ITEM, TestUtils.TABLE_NAME_STORE).withAlias("itemStore").withSelect(true));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria().withFieldName("orderStore.id").withOperator(QCriteriaOperator.NOT_EQUALS).withOtherFieldName("item.storeId")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "# of rows found by query");
QRecord qRecord = queryOutput.getRecords().get(0);
assertEquals(2, qRecord.getValueInteger("id"));
assertEquals(1, qRecord.getValueInteger("orderStore.id"));
assertEquals(2, qRecord.getValueInteger("itemStore.id"));
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// run the same setup, but this time, use the other-field-name as itemStore.id, instead of item.storeId //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter(new QFilterCriteria().withFieldName("orderStore.id").withOperator(QCriteriaOperator.NOT_EQUALS).withOtherFieldName("itemStore.id")));
queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "# of rows found by query");
qRecord = queryOutput.getRecords().get(0);
assertEquals(2, qRecord.getValueInteger("id"));
assertEquals(1, qRecord.getValueInteger("orderStore.id"));
assertEquals(2, qRecord.getValueInteger("itemStore.id"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsQueryByOrderLines() throws Exception
{
AtomicInteger orderLineCount = new AtomicInteger();
runTestSql("SELECT COUNT(*) from order_line", (rs) ->
{
rs.next();
orderLineCount.set(rs.getInt(1));
});
QueryInput queryInput = new QueryInput(TestUtils.defineInstance(), new QSession());
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_LINE, TestUtils.TABLE_NAME_ORDER).withSelect(true));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(orderLineCount.get(), queryOutput.getRecords().size(), "# of rows found by query");
assertEquals(3, queryOutput.getRecords().stream().filter(r -> r.getValueInteger("order.id").equals(1)).count());
assertEquals(1, queryOutput.getRecords().stream().filter(r -> r.getValueInteger("order.id").equals(2)).count());
assertEquals(1, queryOutput.getRecords().stream().filter(r -> r.getValueInteger("orderId").equals(3)).count());
assertEquals(2, queryOutput.getRecords().stream().filter(r -> r.getValueInteger("orderId").equals(4)).count());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsQueryByPersons() throws Exception
{
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput(instance, new QSession());
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
/////////////////////////////////////////////////////
// inner join on bill-to person should find 6 rows //
/////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_PERSON).withJoinMetaData(instance.getJoin("orderJoinBillToPerson")).withSelect(true)));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(6, queryOutput.getRecords().size(), "# of rows found by query");
/////////////////////////////////////////////////////
// inner join on ship-to person should find 7 rows //
/////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withSelect(true)));
queryOutput = new QueryAction().execute(queryInput);
assertEquals(7, queryOutput.getRecords().size(), "# of rows found by query");
/////////////////////////////////////////////////////////////////////////////
// inner join on both bill-to person and ship-to person should find 5 rows //
/////////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true)
));
queryOutput = new QueryAction().execute(queryInput);
assertEquals(5, queryOutput.getRecords().size(), "# of rows found by query");
/////////////////////////////////////////////////////////////////////////////
// left join on both bill-to person and ship-to person should find 8 rows //
/////////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withType(QueryJoin.Type.LEFT).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withType(QueryJoin.Type.LEFT).withAlias("billToPerson").withSelect(true)
));
queryOutput = new QueryAction().execute(queryInput);
assertEquals(8, queryOutput.getRecords().size(), "# of rows found by query");
//////////////////////////////////////////////////
// now join through to personalIdCard table too //
//////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true),
new QueryJoin("billToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToIdCard").withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
queryInput.setFilter(new QQueryFilter()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// look for billToPersons w/ idNumber starting with 1980 - should only be James and Darin (assert on that below). //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
.withCriteria(new QFilterCriteria("billToIdCard.idNumber", QCriteriaOperator.STARTS_WITH, "1980"))
);
queryOutput = new QueryAction().execute(queryInput);
assertEquals(3, queryOutput.getRecords().size(), "# of rows found by query");
assertThat(queryOutput.getRecords().stream().map(r -> r.getValueString("billToPerson.firstName")).toList()).allMatch(p -> p.equals("Darin") || p.equals("James"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsQueryByPersonsExtraKelkhoffOrder() throws Exception
{
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput(instance, new QSession());
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// insert a second person w/ last name Kelkhoff, then an order for Darin Kelkhoff and this new Kelkhoff - //
// then query for orders w/ bill to person & ship to person both lastname = Kelkhoff, but different ids. //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
Integer specialOrderId = 1701;
runTestSql("INSERT INTO person (id, first_name, last_name, email) VALUES (6, 'Jimmy', 'Kelkhoff', 'dk@gmail.com')", null);
runTestSql("INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (" + specialOrderId + ", 1, 1, 6)", null);
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withType(QueryJoin.Type.LEFT).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withType(QueryJoin.Type.LEFT).withAlias("billToPerson").withSelect(true)
));
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria().withFieldName("shipToPerson.lastName").withOperator(QCriteriaOperator.EQUALS).withOtherFieldName("billToPerson.lastName"))
.withCriteria(new QFilterCriteria().withFieldName("shipToPerson.id").withOperator(QCriteriaOperator.NOT_EQUALS).withOtherFieldName("billToPerson.id"))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "# of rows found by query");
assertEquals(specialOrderId, queryOutput.getRecords().get(0).getValueInteger("id"));
////////////////////////////////////////////////////////////
// re-run that query using personIds from the order table //
////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria().withFieldName("shipToPerson.lastName").withOperator(QCriteriaOperator.EQUALS).withOtherFieldName("billToPerson.lastName"))
.withCriteria(new QFilterCriteria().withFieldName("order.shipToPersonId").withOperator(QCriteriaOperator.NOT_EQUALS).withOtherFieldName("order.billToPersonId"))
);
queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "# of rows found by query");
assertEquals(specialOrderId, queryOutput.getRecords().get(0).getValueInteger("id"));
///////////////////////////////////////////////////////////////////////////////////////////////
// re-run that query using personIds from the order table, but not specifying the table name //
///////////////////////////////////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria().withFieldName("shipToPerson.lastName").withOperator(QCriteriaOperator.EQUALS).withOtherFieldName("billToPerson.lastName"))
.withCriteria(new QFilterCriteria().withFieldName("shipToPersonId").withOperator(QCriteriaOperator.NOT_EQUALS).withOtherFieldName("billToPersonId"))
);
queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "# of rows found by query");
assertEquals(specialOrderId, queryOutput.getRecords().get(0).getValueInteger("id"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testDuplicateAliases()
{
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput(instance, new QSession());
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson"),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson"),
new QueryJoin("billToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withSelect(true) // w/o alias, should get exception here - dupe table.
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.hasRootCauseMessage("Duplicate table name or alias: personalIdCard");
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson"),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson"),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToPerson").withSelect(true), // dupe alias, should get exception here
new QueryJoin("billToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToPerson").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.hasRootCauseMessage("Duplicate table name or alias: shipToPerson");
}
}

View File

@ -41,6 +41,23 @@ INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, a
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (4, 'Tyler', 'Samples', NULL, 'tsamples@mmltholdings.com', 1, 30000, 99);
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com', 1, 1000000, 232);
DROP TABLE IF EXISTS personal_id_card;
CREATE TABLE personal_id_card
(
id INT AUTO_INCREMENT primary key ,
create_date TIMESTAMP DEFAULT now(),
modify_date TIMESTAMP DEFAULT now(),
person_id INTEGER,
id_number VARCHAR(250)
);
INSERT INTO personal_id_card (person_id, id_number) VALUES (1, '19800531');
INSERT INTO personal_id_card (person_id, id_number) VALUES (2, '19800515');
INSERT INTO personal_id_card (person_id, id_number) VALUES (3, '19760528');
INSERT INTO personal_id_card (person_id, id_number) VALUES (6, '123123123');
INSERT INTO personal_id_card (person_id, id_number) VALUES (null, '987987987');
INSERT INTO personal_id_card (person_id, id_number) VALUES (null, '456456456');
DROP TABLE IF EXISTS carrier;
CREATE TABLE carrier
(
@ -61,3 +78,77 @@ INSERT INTO carrier (id, name, company_code, service_level) VALUES (8, 'USPS Sup
INSERT INTO carrier (id, name, company_code, service_level) VALUES (9, 'USPS Super Fast', 'USPS', '0');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (10, 'DHL International', 'DHL', 'I');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (11, 'GSO', 'GSO', 'G');
DROP TABLE IF EXISTS order_line;
DROP TABLE IF EXISTS item;
DROP TABLE IF EXISTS `order`;
DROP TABLE IF EXISTS store;
CREATE TABLE store
(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(80) NOT NULL
);
-- define 3 stores
INSERT INTO store (id, name) VALUES (1, 'Q-Mart');
INSERT INTO store (id, name) VALUES (2, 'QQQ ''R'' Us');
INSERT INTO store (id, name) VALUES (3, 'QDepot');
CREATE TABLE item
(
id INT AUTO_INCREMENT PRIMARY KEY,
sku VARCHAR(80) NOT NULL,
store_id INT NOT NULL REFERENCES store
);
-- three items for each store
INSERT INTO item (id, sku, store_id) VALUES (1, 'QM-1', 1);
INSERT INTO item (id, sku, store_id) VALUES (2, 'QM-2', 1);
INSERT INTO item (id, sku, store_id) VALUES (3, 'QM-3', 1);
INSERT INTO item (id, sku, store_id) VALUES (4, 'QRU-1', 2);
INSERT INTO item (id, sku, store_id) VALUES (5, 'QRU-2', 2);
INSERT INTO item (id, sku, store_id) VALUES (6, 'QRU-3', 2);
INSERT INTO item (id, sku, store_id) VALUES (7, 'QD-1', 3);
INSERT INTO item (id, sku, store_id) VALUES (8, 'QD-2', 3);
INSERT INTO item (id, sku, store_id) VALUES (9, 'QD-3', 3);
CREATE TABLE `order`
(
id INT AUTO_INCREMENT PRIMARY KEY,
store_id INT REFERENCES store,
bill_to_person_id INT,
ship_to_person_id INT
);
-- variable orders
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (1, 1, 1, 1);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (2, 1, 1, 2);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (3, 1, 2, 3);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (4, 2, 4, 5);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (5, 2, 5, 4);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (6, 3, 5, null);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (7, 3, null, 5);
INSERT INTO `order` (id, store_id, bill_to_person_id, ship_to_person_id) VALUES (8, 3, null, 5);
CREATE TABLE order_line
(
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT REFERENCES `order`,
sku VARCHAR(80),
store_id INT REFERENCES store, -- todo - as a challenge, if this field wasn't here, so we had to join through order...
quantity INT
);
-- various lines
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (1, 'QM-1', 1, 10);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (1, 'QM-2', 1, 1);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (1, 'QM-3', 1, 1);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (2, 'QRU-1', 2, 1); -- this line has an item from a different store than its order.
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (3, 'QM-1', 1, 20);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (4, 'QRU-1', 2, 1);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (4, 'QRU-2', 2, 2);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (5, 'QRU-1', 2, 1);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (6, 'QD-1', 3, 1);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (7, 'QD-1', 3, 2);
INSERT INTO order_line (order_id, sku, store_id, quantity) VALUES (8, 'QD-1', 3, 3);