Join Enhancements:

- Moving responsibility for adding security clauses out of AbstractRDBMSAction, into JoinsContext
- Adding QueryJoin securityClauses (helps outer-join security filtering work as expected)
- Add security clauses for all joined tables
- Improved inferring of joinMetaData, especially from ExposedJoins
- Fix processes use of selectDistinct when ordering by a field from a joinTable (by doing the Distinct in the record pipe)
This commit is contained in:
2023-09-07 12:23:12 -05:00
parent d9458ced34
commit 73e826f81d
22 changed files with 2055 additions and 1124 deletions

View File

@ -0,0 +1,85 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.reporting;
import java.io.ByteArrayOutputStream;
import java.util.List;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportOutput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSActionTest;
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.assertNotNull;
/*******************************************************************************
** Test some harder exports, using RDBMS backend.
*******************************************************************************/
public class ExportActionWithinRDBMSTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testIncludingFieldsFromExposedJoinTableWithTwoJoinsToMainTable() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ExportInput exportInput = new ExportInput();
exportInput.setTableName(TestUtils.TABLE_NAME_ORDER);
exportInput.setReportFormat(ReportFormat.CSV);
exportInput.setReportOutputStream(baos);
exportInput.setQueryFilter(new QQueryFilter());
exportInput.setFieldNames(List.of("id", "storeId", "billToPersonId", "currentOrderInstructionsId", TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS + ".id", TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS + ".instructions"));
ExportOutput exportOutput = new ExportAction().execute(exportInput);
assertNotNull(exportOutput);
///////////////////////////////////////////////////////////////////////////
// if there was an exception running the query, we get back 0 records... //
///////////////////////////////////////////////////////////////////////////
assertEquals(3, exportOutput.getRecordCount());
}
}

View File

@ -243,6 +243,7 @@ public class TestUtils
.withRecordSecurityLock(new RecordSecurityLock().withSecurityKeyType(TABLE_NAME_STORE).withFieldName("storeId"))
.withAssociation(new Association().withName("orderLine").withAssociatedTableName(TABLE_NAME_ORDER_LINE).withJoinName("orderJoinOrderLine"))
.withExposedJoin(new ExposedJoin().withJoinTable(TABLE_NAME_ITEM).withJoinPath(List.of("orderJoinOrderLine", "orderLineJoinItem")))
.withExposedJoin(new ExposedJoin().withJoinTable(TABLE_NAME_ORDER_INSTRUCTIONS).withJoinPath(List.of("orderJoinCurrentOrderInstructions")))
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withBackendName("store_id").withPossibleValueSourceName(TABLE_NAME_STORE))
.withField(new QFieldMetaData("billToPersonId", QFieldType.INTEGER).withBackendName("bill_to_person_id").withPossibleValueSourceName(TABLE_NAME_PERSON))
.withField(new QFieldMetaData("shipToPersonId", QFieldType.INTEGER).withBackendName("ship_to_person_id").withPossibleValueSourceName(TABLE_NAME_PERSON))

View File

@ -161,10 +161,10 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
insertInput.setRecords(List.of(
new QRecord().withValue("storeId", 1).withValue("billToPersonId", 100).withValue("shipToPersonId", 200)
.withAssociatedRecord("orderLine", new QRecord().withValue("sku", "BASIC1").withValue("quantity", 1)
.withAssociatedRecord("orderLine", new QRecord().withValue("storeId", 1).withValue("sku", "BASIC1").withValue("quantity", 1)
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-1.1").withValue("value", "LINE-VAL-1")))
.withAssociatedRecord("orderLine", new QRecord().withValue("sku", "BASIC2").withValue("quantity", 2)
.withAssociatedRecord("orderLine", new QRecord().withValue("storeId", 1).withValue("sku", "BASIC2").withValue("quantity", 2)
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-2.1").withValue("value", "LINE-VAL-2"))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-2.2").withValue("value", "LINE-VAL-3")))
));

View File

@ -0,0 +1,987 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
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.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.metadata.security.RecordSecurityLock;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.AfterEach;
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;
/*******************************************************************************
** Tests on RDBMS - specifically dealing with Joins.
*******************************************************************************/
public class RDBMSQueryActionJoinsTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
AbstractRDBMSAction.setLogSQL(true);
AbstractRDBMSAction.setLogSQLOutput("system.out");
}
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
void afterEach()
{
AbstractRDBMSAction.setLogSQL(false);
}
/*******************************************************************************
**
*******************************************************************************/
private QueryInput initQueryRequest()
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_PERSON);
return queryInput;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFilterFromJoinTableImplicitly() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("personalIdCard.idNumber", QCriteriaOperator.EQUALS, "19800531")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "Query should find 1 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithoutWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(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_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_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_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" + StringUtils.ucFirst(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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
queryInput.withQueryJoin(new QueryJoin(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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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_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"));
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ensure we throw if either of the ambiguous joins from person to id-card doesn't specify its left-table //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true),
new QueryJoin(TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToIdCard").withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.rootCause()
.hasMessageContaining("Could not find a join between tables [order][personalIdCard]");
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ensure we throw if either of the ambiguous joins from person to id-card doesn't specify its left-table //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
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(TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.rootCause()
.hasMessageContaining("Could not find a join between tables [order][personalIdCard]");
////////////////////////////////////////////////////////////////////////
// ensure we throw if we have a bogus alias name given as a left-side //
////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true),
new QueryJoin("notATable", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToIdCard").withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.hasRootCauseMessage("Could not find a join between tables [notATable][personalIdCard]");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsQueryByPersonsExtraKelkhoffOrder() throws Exception
{
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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();
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");
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, also selecting item.
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoin() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ITEM).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("id") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ITEM + ".description") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on item to order
** do a query on item, also selecting order.
** This is a reverse of the above, to make sure join flipping, etc, is good.
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinReversed() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ITEM);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ORDER).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("description") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ORDER + ".id") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, also selecting item, and also selecting orderLine...
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinAlsoSelectingInBetweenTable() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ORDER_LINE).withType(QueryJoin.Type.INNER).withSelect(true),
new QueryJoin(TestUtils.TABLE_NAME_ITEM).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("id") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ITEM + ".description") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, filtered by item
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinWhereClauseOnly() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(TestUtils.TABLE_NAME_ITEM + ".description", QCriteriaOperator.STARTS_WITH, "Q-Mart")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(4);
assertThat(records).allMatch(r -> r.getValue("id") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, filtered by item
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinWhereClauseBothJoinTables() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria(TestUtils.TABLE_NAME_ITEM + ".description", QCriteriaOperator.STARTS_WITH, "Q-Mart"))
.withCriteria(new QFilterCriteria(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity", QCriteriaOperator.IS_NOT_BLANK))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(4);
assertThat(records).allMatch(r -> r.getValue("id") != null);
}
/*******************************************************************************
** queries on the store table, where the primary key (id) is the security field
*******************************************************************************/
@Test
void testRecordSecurityPrimaryKeyFieldNoFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_STORE);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(3);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.anyMatch(r -> r.getValueInteger("id").equals(1));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.anyMatch(r -> r.getValueInteger("id").equals(2));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.anyMatch(r -> r.getValueInteger("id").equals(1))
.anyMatch(r -> r.getValueInteger("id").equals(3));
}
/*******************************************************************************
** not really expected to be any different from where we filter on the primary key,
** but just good to make sure
*******************************************************************************/
@Test
void testRecordSecurityForeignKeyFieldNoFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(8);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(2));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(6)
.allMatch(r -> r.getValueInteger("storeId").equals(1) || r.getValueInteger("storeId").equals(3));
}
/*******************************************************************************
** Error seen in CTLive - query for order join lineItem, where lineItem's security
** key is in order.
**
** Note - in this test-db setup, there happens to be a storeId in both order &
** orderLine tables, so we can't quite reproduce the error we saw in CTL - so
** query on different tables with the structure that'll produce the error.
*******************************************************************************/
@Test
void testRequestedJoinWithTableWhoseSecurityFieldIsInMainTable() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE_STORE_INT);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_WAREHOUSE).withSelect(true));
//////////////////////////////////////////////
// with the all-access key, find all 3 rows //
//////////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(3);
///////////////////////////////////////////
// with 1 security key value, find 1 row //
///////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
///////////////////////////////////////////
// with 1 security key value, find 1 row //
///////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.allMatch(r -> r.getValueInteger("storeId").equals(2));
//////////////////////////////////////////////////////////
// with a mis-matching security key value, 0 rows found //
//////////////////////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
///////////////////////////////////////////////
// with no security key values, 0 rows found //
///////////////////////////////////////////////
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
////////////////////////////////////////////////
// with null security key value, 0 rows found //
////////////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
//////////////////////////////////////////////////////
// with empty-list security key value, 0 rows found //
//////////////////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
////////////////////////////////
// with 2 values, find 2 rows //
////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(1) || r.getValueInteger("storeId").equals(3));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityFromJoinTableAlsoImplicitlyInQuery() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
///////////////////////////////////////////////////////////////////////////////////////
// orders 1, 2, and 3 are from store 1, so their lines (5 in total) should be found. //
// note, order 2 has the line with mis-matched store id - but, that shouldn't apply //
// here, because the line table's security comes from the order table. //
///////////////////////////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("order.id", QCriteriaOperator.IN, List.of(1, 2, 3, 4))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(5);
///////////////////////////////////////////////////////////////////
// order 4 should be the only one found this time (with 2 lines) //
///////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("order.id", QCriteriaOperator.IN, List.of(1, 2, 3, 4))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(2);
////////////////////////////////////////////////////////////////
// make sure we're also good if we explicitly join this table //
////////////////////////////////////////////////////////////////
queryInput.withQueryJoin(new QueryJoin().withJoinTable(TestUtils.TABLE_NAME_ORDER).withSelect(true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(2);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithLockFromJoinTable() throws QException
{
QInstance qInstance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
/////////////////////////////////////////////////////////////////////////////////////////////////
// remove the normal lock on the order table - replace it with one from the joined store table //
/////////////////////////////////////////////////////////////////////////////////////////////////
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().clear();
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withRecordSecurityLock(new RecordSecurityLock()
.withSecurityKeyType(TestUtils.TABLE_NAME_STORE)
.withJoinNameChain(List.of("orderJoinStore"))
.withFieldName("store.id"));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithLockFromJoinTableWhereTheKeyIsOnTheManySide() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMultipleReversedDirectionJoinsBetweenSameTables() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
Integer noOfOrders = new CountAction().execute(new CountInput(TestUtils.TABLE_NAME_ORDER)).getCount();
Integer noOfOrderInstructions = new CountAction().execute(new CountInput(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS)).getCount();
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// make sure we can join on order.current_order_instruction_id = order_instruction.id -- and that we get back 1 row per order //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS).withJoinMetaData(QContext.getQInstance().getJoin("orderJoinCurrentOrderInstructions")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(noOfOrders, queryOutput.getRecords().size());
}
{
////////////////////////////////////////////////////////////////////////////////////////////////
// assert that the query succeeds (based on exposed join) if the joinMetaData isn't specified //
////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(noOfOrders, queryOutput.getRecords().size());
}
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// make sure we can join on order.id = order_instruction.order_id (e.g., not the exposed one used above) -- and that we get back 1 row per order instruction //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS).withJoinMetaData(QContext.getQInstance().getJoin("orderInstructionsJoinOrder")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(noOfOrderInstructions, queryOutput.getRecords().size());
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testSecurityJoinForJoinedTableFromImplicitlyJoinedTable() throws QException
{
/////////////////////////////////////////////////////////////////////////////////////////
// in this test: //
// query on Order, joined with OrderLine. //
// Order has its own security field (storeId), that's always worked fine. //
// We want to change OrderLine's security field to be item.storeId - not order.storeId //
// so that item has to be brought into the query to secure the items. //
// this was originally broken, as it would generate a WHERE clause for item.storeId, //
// but it wouldn't put item in the FROM cluase.
/////////////////////////////////////////////////////////////////////////////////////////
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_ORDER_LINE)
.setRecordSecurityLocks(ListBuilder.of(
new RecordSecurityLock()
.withSecurityKeyType(TestUtils.TABLE_NAME_STORE)
.withFieldName("item.storeId")
.withJoinNameChain(List.of("orderLineJoinItem"))));
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_LINE).withSelect(true));
queryInput.withFilter(new QQueryFilter(new QFilterCriteria(TestUtils.TABLE_NAME_ORDER_LINE + ".sku", QCriteriaOperator.IS_NOT_BLANK)));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertEquals(3, records.size(), "expected no of records");
///////////////////////////////////////////////////////////////////////
// we should get the orderLines for orders 4 and 5 - but not the one //
// for order 2, as it has an item from a different store //
///////////////////////////////////////////////////////////////////////
assertThat(records).allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5));
}
}

View File

@ -25,26 +25,20 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.Serializable;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
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.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.actions.tables.query.expressions.Now;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.NowWithOffset;
@ -52,14 +46,12 @@ 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.security.RecordSecurityLock;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.AfterEach;
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;
@ -783,675 +775,6 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFilterFromJoinTableImplicitly() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("personalIdCard.idNumber", QCriteriaOperator.EQUALS, "19800531")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size(), "Query should find 1 rows");
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOneToOneInnerJoinWithoutWhere() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.withQueryJoin(new QueryJoin(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_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_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_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" + StringUtils.ucFirst(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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
queryInput.withQueryJoin(new QueryJoin(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();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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_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"));
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ensure we throw if either of the ambiguous joins from person to id-card doesn't specify its left-table //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true),
new QueryJoin(TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToIdCard").withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.rootCause()
.hasMessageContaining("Could not find a join between tables [order][personalIdCard]");
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ensure we throw if either of the ambiguous joins from person to id-card doesn't specify its left-table //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
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(TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.rootCause()
.hasMessageContaining("Could not find a join between tables [order][personalIdCard]");
////////////////////////////////////////////////////////////////////////
// ensure we throw if we have a bogus alias name given as a left-side //
////////////////////////////////////////////////////////////////////////
queryInput.withQueryJoins(List.of(
new QueryJoin(instance.getJoin("orderJoinShipToPerson")).withAlias("shipToPerson").withSelect(true),
new QueryJoin(instance.getJoin("orderJoinBillToPerson")).withAlias("billToPerson").withSelect(true),
new QueryJoin("notATable", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("billToIdCard").withSelect(true),
new QueryJoin("shipToPerson", TestUtils.TABLE_NAME_PERSONAL_ID_CARD).withAlias("shipToIdCard").withSelect(true)
));
assertThatThrownBy(() -> new QueryAction().execute(queryInput))
.hasRootCauseMessage("Could not find a join between tables [notATable][personalIdCard]");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOmsQueryByPersonsExtraKelkhoffOrder() throws Exception
{
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
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();
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");
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, also selecting item.
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoin() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ITEM).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("id") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ITEM + ".description") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on item to order
** do a query on item, also selecting order.
** This is a reverse of the above, to make sure join flipping, etc, is good.
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinReversed() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ITEM);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ORDER).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("description") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ORDER + ".id") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, also selecting item, and also selecting orderLine...
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinAlsoSelectingInBetweenTable() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoins(List.of(
new QueryJoin(TestUtils.TABLE_NAME_ORDER_LINE).withType(QueryJoin.Type.INNER).withSelect(true),
new QueryJoin(TestUtils.TABLE_NAME_ITEM).withType(QueryJoin.Type.INNER).withSelect(true)
));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(11); // one per line item
assertThat(records).allMatch(r -> r.getValue("id") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity") != null);
assertThat(records).allMatch(r -> r.getValue(TestUtils.TABLE_NAME_ITEM + ".description") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, filtered by item
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinWhereClauseOnly() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(TestUtils.TABLE_NAME_ITEM + ".description", QCriteriaOperator.STARTS_WITH, "Q-Mart")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(4);
assertThat(records).allMatch(r -> r.getValue("id") != null);
}
/*******************************************************************************
** Given tables:
** order - orderLine - item
** with exposedJoin on order to item
** do a query on order, filtered by item
*******************************************************************************/
@Test
void testTwoTableAwayExposedJoinWhereClauseBothJoinTables() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QInstance instance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria(TestUtils.TABLE_NAME_ITEM + ".description", QCriteriaOperator.STARTS_WITH, "Q-Mart"))
.withCriteria(new QFilterCriteria(TestUtils.TABLE_NAME_ORDER_LINE + ".quantity", QCriteriaOperator.IS_NOT_BLANK))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
List<QRecord> records = queryOutput.getRecords();
assertThat(records).hasSize(4);
assertThat(records).allMatch(r -> r.getValue("id") != null);
}
/*******************************************************************************
** queries on the store table, where the primary key (id) is the security field
*******************************************************************************/
@Test
void testRecordSecurityPrimaryKeyFieldNoFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_STORE);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(3);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.anyMatch(r -> r.getValueInteger("id").equals(1));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1)
.anyMatch(r -> r.getValueInteger("id").equals(2));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.anyMatch(r -> r.getValueInteger("id").equals(1))
.anyMatch(r -> r.getValueInteger("id").equals(3));
}
/*******************************************************************************
** not really expected to be any different from where we filter on the primary key,
** but just good to make sure
*******************************************************************************/
@Test
void testRecordSecurityForeignKeyFieldNoFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(8);
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(2));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, null));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, Collections.emptyList()));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(6)
.allMatch(r -> r.getValueInteger("storeId").equals(1) || r.getValueInteger("storeId").equals(3));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithFilters() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityFromJoinTableAlsoImplicitlyInQuery() throws QException
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER_LINE);
///////////////////////////////////////////////////////////////////////////////////////////
// orders 1, 2, and 3 are from store 1, so their lines (5 in total) should be found. //
// note, order 2 has the line with mis-matched store id - but, that shouldn't apply here //
///////////////////////////////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("order.id", QCriteriaOperator.IN, List.of(1, 2, 3, 4))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(5);
///////////////////////////////////////////////////////////////////
// order 4 should be the only one found this time (with 2 lines) //
///////////////////////////////////////////////////////////////////
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("order.id", QCriteriaOperator.IN, List.of(1, 2, 3, 4))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 2));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(2);
////////////////////////////////////////////////////////////////
// make sure we're also good if we explicitly join this table //
////////////////////////////////////////////////////////////////
queryInput.withQueryJoin(new QueryJoin().withJoinTable(TestUtils.TABLE_NAME_ORDER).withSelect(true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(2);
}
/*******************************************************************************
**
*******************************************************************************/
@ -1606,68 +929,6 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithLockFromJoinTable() throws QException
{
QInstance qInstance = TestUtils.defineInstance();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
/////////////////////////////////////////////////////////////////////////////////////////////////
// remove the normal lock on the order table - replace it with one from the joined store table //
/////////////////////////////////////////////////////////////////////////////////////////////////
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().clear();
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withRecordSecurityLock(new RecordSecurityLock()
.withSecurityKeyType(TestUtils.TABLE_NAME_STORE)
.withJoinNameChain(List.of("orderJoinStore"))
.withFieldName("store.id"));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
assertThat(new QueryAction().execute(queryInput).getRecords()).hasSize(6);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 1));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(2)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.TABLE_NAME_STORE, 5));
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.BETWEEN, List.of(2, 7))));
QContext.setQSession(new QSession());
assertThat(new QueryAction().execute(queryInput).getRecords()).isEmpty();
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("storeId", QCriteriaOperator.IN, List.of(1, 2))));
QContext.setQSession(new QSession().withSecurityKeyValues(TestUtils.TABLE_NAME_STORE, List.of(1, 3)));
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(3)
.allMatch(r -> r.getValueInteger("storeId").equals(1));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRecordSecurityWithLockFromJoinTableWhereTheKeyIsOnTheManySide() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_WAREHOUSE);
assertThat(new QueryAction().execute(queryInput).getRecords())
.hasSize(1);
}
/*******************************************************************************
**
*******************************************************************************/
@ -1697,51 +958,4 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMultipleReversedDirectionJoinsBetweenSameTables() throws QException
{
QContext.setQSession(new QSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_STORE_ALL_ACCESS, true));
{
/////////////////////////////////////////////////////////
// assert a failure if the join to use isn't specified //
/////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS));
assertThatThrownBy(() -> new QueryAction().execute(queryInput)).rootCause().hasMessageContaining("More than 1 join was found");
}
Integer noOfOrders = new CountAction().execute(new CountInput(TestUtils.TABLE_NAME_ORDER)).getCount();
Integer noOfOrderInstructions = new CountAction().execute(new CountInput(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS)).getCount();
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// make sure we can join on order.current_order_instruction_id = order_instruction.id -- and that we get back 1 row per order //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS).withJoinMetaData(QContext.getQInstance().getJoin("orderJoinCurrentOrderInstructions")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(noOfOrders, queryOutput.getRecords().size());
}
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// make sure we can join on order.id = order_instruction.order_id -- and that we get back 1 row per order instruction //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput();
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
queryInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER_INSTRUCTIONS).withJoinMetaData(QContext.getQInstance().getJoin("orderInstructionsJoinOrder")));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(noOfOrderInstructions, queryOutput.getRecords().size());
}
}
}