mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-21 06:28:44 +00:00
Compare commits
2 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
9afbe7c3a9 | |||
e1fd6d51c4 |
@ -44,7 +44,6 @@ import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
|
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.ExportOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
@ -217,8 +216,7 @@ public class ExportAction
|
|||||||
}
|
}
|
||||||
queryInput.getFilter().setLimit(exportInput.getLimit());
|
queryInput.getFilter().setLimit(exportInput.getLimit());
|
||||||
queryInput.setShouldTranslatePossibleValues(true);
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
queryInput.withQueryHint(QueryInput.QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
||||||
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// tell this query that it needs to put its output into a pipe //
|
// tell this query that it needs to put its output into a pipe //
|
||||||
|
@ -59,7 +59,6 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
||||||
@ -418,8 +417,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
queryInput.setTableName(dataSource.getSourceTable());
|
queryInput.setTableName(dataSource.getSourceTable());
|
||||||
queryInput.setFilter(queryFilter);
|
queryInput.setFilter(queryFilter);
|
||||||
queryInput.setQueryJoins(dataSource.getQueryJoins());
|
queryInput.setQueryJoins(dataSource.getQueryJoins());
|
||||||
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
queryInput.withQueryHint(QueryInput.QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
||||||
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
|
|
||||||
queryInput.setShouldTranslatePossibleValues(true);
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
queryInput.setFieldsToTranslatePossibleValues(setupFieldsToTranslatePossibleValues(reportInput, dataSource, new JoinsContext(reportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryInput.getFilter())));
|
queryInput.setFieldsToTranslatePossibleValues(setupFieldsToTranslatePossibleValues(reportInput, dataSource, new JoinsContext(reportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryInput.getFilter())));
|
||||||
|
@ -32,12 +32,13 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
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.QCriteriaOperator;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
@ -77,6 +78,45 @@ public class QPossibleValueTranslator
|
|||||||
|
|
||||||
private int maxSizePerPvsCache = 50_000;
|
private int maxSizePerPvsCache = 50_000;
|
||||||
|
|
||||||
|
private Map<String, QBackendTransaction> transactionsPerTable = new HashMap<>();
|
||||||
|
|
||||||
|
// todo not commit - remove instance & session - use Context
|
||||||
|
|
||||||
|
|
||||||
|
boolean useTransactionsAsConnectionPool = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private QBackendTransaction getTransaction(String tableName)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// mmm, this does cut down on connections used - //
|
||||||
|
// especially seems helpful in big exports. //
|
||||||
|
// but, let's just start using connection pools instead... //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
if(useTransactionsAsConnectionPool)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!transactionsPerTable.containsKey(tableName))
|
||||||
|
{
|
||||||
|
transactionsPerTable.put(tableName, QBackendTransaction.openFor(new InsertInput(tableName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (transactionsPerTable.get(tableName));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error opening transaction for table", logPair("tableName", tableName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -561,7 +601,7 @@ public class QPossibleValueTranslator
|
|||||||
QueryInput queryInput = new QueryInput();
|
QueryInput queryInput = new QueryInput();
|
||||||
queryInput.setTableName(tableName);
|
queryInput.setTableName(tableName);
|
||||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(idField, QCriteriaOperator.IN, page)));
|
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(idField, QCriteriaOperator.IN, page)));
|
||||||
queryInput.hasQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
queryInput.setTransaction(getTransaction(tableName));
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// when querying for possible values, we do want to generate their display values, which makes record labels, which are usually used as PVS labels //
|
// when querying for possible values, we do want to generate their display values, which makes record labels, which are usually used as PVS labels //
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.model.actions.tables;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Information about the query that an application (or qqq service) may know and
|
|
||||||
** want to tell the backend, that can help influence how the backend processes
|
|
||||||
** query.
|
|
||||||
**
|
|
||||||
** For example, a query with potentially a large result set, for MySQL backend,
|
|
||||||
** we may want to configure the result set to stream results rather than do its
|
|
||||||
** default in-memory thing. See RDBMSQueryAction for usage.
|
|
||||||
*******************************************************************************/
|
|
||||||
public enum QueryHint
|
|
||||||
{
|
|
||||||
POTENTIALLY_LARGE_NUMBER_OF_RESULTS,
|
|
||||||
MAY_USE_READ_ONLY_BACKEND
|
|
||||||
}
|
|
@ -23,10 +23,8 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.aggregate;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||||
|
|
||||||
@ -46,8 +44,6 @@ public class AggregateInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
private List<QueryJoin> queryJoins = null;
|
private List<QueryJoin> queryJoins = null;
|
||||||
|
|
||||||
private EnumSet<QueryHint> queryHints = EnumSet.noneOf(QueryHint.class);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -306,78 +302,4 @@ public class AggregateInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public EnumSet<QueryHint> getQueryHints()
|
|
||||||
{
|
|
||||||
return (this.queryHints);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setQueryHints(EnumSet<QueryHint> queryHints)
|
|
||||||
{
|
|
||||||
this.queryHints = queryHints;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public AggregateInput withQueryHints(EnumSet<QueryHint> queryHints)
|
|
||||||
{
|
|
||||||
this.queryHints = queryHints;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public AggregateInput withQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints == null)
|
|
||||||
{
|
|
||||||
this.queryHints = EnumSet.noneOf(QueryHint.class);
|
|
||||||
}
|
|
||||||
this.queryHints.add(queryHint);
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public AggregateInput withoutQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints != null)
|
|
||||||
{
|
|
||||||
this.queryHints.remove(queryHint);
|
|
||||||
}
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** null-safely check if query hints map contains the specified hint
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean hasQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints == null)
|
|
||||||
{
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (queryHints.contains(queryHint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,8 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.count;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||||
|
|
||||||
@ -44,8 +42,6 @@ public class CountInput extends AbstractTableActionInput
|
|||||||
private List<QueryJoin> queryJoins = null;
|
private List<QueryJoin> queryJoins = null;
|
||||||
private Boolean includeDistinctCount = false;
|
private Boolean includeDistinctCount = false;
|
||||||
|
|
||||||
private EnumSet<QueryHint> queryHints = EnumSet.noneOf(QueryHint.class);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -211,78 +207,4 @@ public class CountInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public EnumSet<QueryHint> getQueryHints()
|
|
||||||
{
|
|
||||||
return (this.queryHints);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setQueryHints(EnumSet<QueryHint> queryHints)
|
|
||||||
{
|
|
||||||
this.queryHints = queryHints;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public CountInput withQueryHints(EnumSet<QueryHint> queryHints)
|
|
||||||
{
|
|
||||||
this.queryHints = queryHints;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public CountInput withQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints == null)
|
|
||||||
{
|
|
||||||
this.queryHints = EnumSet.noneOf(QueryHint.class);
|
|
||||||
}
|
|
||||||
this.queryHints.add(queryHint);
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for queryHints
|
|
||||||
*******************************************************************************/
|
|
||||||
public CountInput withoutQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints != null)
|
|
||||||
{
|
|
||||||
this.queryHints.remove(queryHint);
|
|
||||||
}
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** null-safely check if query hints map contains the specified hint
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean hasQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints == null)
|
|
||||||
{
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (queryHints.contains(queryHint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -31,16 +31,12 @@ import java.util.Set;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryOrGetInputInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryOrGetInputInterface;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Input data for the Query action
|
** Input data for the Query action
|
||||||
**
|
**
|
||||||
** Todo - maybe make a class between AbstractTableActionInput and {QueryInput,
|
|
||||||
** CountInput, and AggregateInput}, with common attributes for all of these
|
|
||||||
** "read" operations (like, queryHints,
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QueryInput extends AbstractTableActionInput implements QueryOrGetInputInterface, Cloneable
|
public class QueryInput extends AbstractTableActionInput implements QueryOrGetInputInterface, Cloneable
|
||||||
{
|
{
|
||||||
@ -78,6 +74,22 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Information about the query that an application (or qqq service) may know and
|
||||||
|
** want to tell the backend, that can help influence how the backend processes
|
||||||
|
** query.
|
||||||
|
**
|
||||||
|
** For example, a query with potentially a large result set, for MySQL backend,
|
||||||
|
** we may want to configure the result set to stream results rather than do its
|
||||||
|
** default in-memory thing. See RDBMSQueryAction for usage.
|
||||||
|
*******************************************************************************/
|
||||||
|
public enum QueryHint
|
||||||
|
{
|
||||||
|
POTENTIALLY_LARGE_NUMBER_OF_RESULTS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -671,19 +683,4 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** null-safely check if query hints map contains the specified hint
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean hasQueryHint(QueryHint queryHint)
|
|
||||||
{
|
|
||||||
if(this.queryHints == null)
|
|
||||||
{
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (queryHints.contains(queryHint));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.model.session;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Special session, indicating that an action being executed is being done not
|
||||||
|
** on behalf of a (human or otherwise) user - but instead, is the application/
|
||||||
|
** system itself.
|
||||||
|
**
|
||||||
|
** Generally this means, escalated privileges - e.g., permission to all tables,
|
||||||
|
** processes etc, and all security keys (e.g., all-access keys).
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QSystemUserSession extends QSession
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(QSystemUserSession.class);
|
||||||
|
|
||||||
|
private static List<String> allAccessKeyNames = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QSystemUserSession()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// always give system user all of the all-access keys //
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
for(String allAccessKeyName : getAllAccessKeyNames())
|
||||||
|
{
|
||||||
|
withSecurityKeyValue(allAccessKeyName, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** System User Sessions should always have permission to all the things.
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permissionName)
|
||||||
|
{
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private List<String> getAllAccessKeyNames()
|
||||||
|
{
|
||||||
|
if(allAccessKeyNames == null)
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
if(qInstance == null)
|
||||||
|
{
|
||||||
|
LOG.warn("QInstance was not set in context when creating a QSystemUserSession and trying to prime allAccessKeyNames... This SystemUserSession will NOT have any allAccessKeys.");
|
||||||
|
return (Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ideally only 1 thread would do this, but, it's cheap, so don't bother locking. //
|
||||||
|
// and, if multiple get in, only the last one will assign to the field, so, s/b fine //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for(QSecurityKeyType securityKeyType : qInstance.getSecurityKeyTypes().values())
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||||
|
{
|
||||||
|
list.add(securityKeyType.getAllAccessKeyName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Initialized allAccessKeyNames for SystemUserSessions as: " + list);
|
||||||
|
allAccessKeyNames = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (allAccessKeyNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Meant for use in tests - to explicitly null-out the allAccessKeyNames field.
|
||||||
|
*******************************************************************************/
|
||||||
|
static void unsetAllAccessKeyNames()
|
||||||
|
{
|
||||||
|
allAccessKeyNames = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -72,6 +72,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
|
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
|
||||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||||
@ -491,6 +492,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
|||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(session instanceof QSystemUserSession)
|
||||||
|
{
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
if(session == null)
|
if(session == null)
|
||||||
{
|
{
|
||||||
return (false);
|
return (false);
|
||||||
|
@ -51,6 +51,7 @@ 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.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAuthenticationMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAuthenticationMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||||
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||||
@ -256,6 +257,11 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
|
|||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(session instanceof QSystemUserSession)
|
||||||
|
{
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
if(session == null)
|
if(session == null)
|
||||||
{
|
{
|
||||||
return (false);
|
return (false);
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.garbagecollecto
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
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.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||||
@ -67,7 +66,7 @@ public class GarbageCollectorExtractStep extends ExtractViaQueryStep
|
|||||||
@Override
|
@Override
|
||||||
protected void customizeInputPreQuery(QueryInput queryInput)
|
protected void customizeInputPreQuery(QueryInput queryInput)
|
||||||
{
|
{
|
||||||
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
queryInput.withQueryHint(QueryInput.QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,18 @@ package com.kingsrook.qqq.backend.core.scheduler.quartz.processes;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
|
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||||
@ -43,33 +46,39 @@ import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class PauseQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<QProcessMetaData>
|
public class PauseQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<MetaDataProducerMultiOutput>
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public QProcessMetaData produce(QInstance qInstance) throws QException
|
public MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException
|
||||||
{
|
{
|
||||||
String tableName = "quartzJobDetails";
|
BiFunction<String, String, QProcessMetaData> processMaker = (String tableName, String label) ->
|
||||||
|
StreamedETLWithFrontendProcess.processMetaDataBuilder()
|
||||||
|
.withName(getClass().getSimpleName())
|
||||||
|
.withLabel(label)
|
||||||
|
.withPreviewMessage("This is a preview of the jobs that will be paused.")
|
||||||
|
.withTableName(tableName)
|
||||||
|
.withSourceTable(tableName)
|
||||||
|
.withDestinationTable(tableName)
|
||||||
|
.withExtractStepClass(ExtractViaQueryStep.class)
|
||||||
|
.withTransformStepClass(NoopTransformStep.class)
|
||||||
|
.withLoadStepClass(getClass())
|
||||||
|
.withIcon(new QIcon("pause_circle_outline"))
|
||||||
|
.withReviewStepRecordFields(List.of(
|
||||||
|
new QFieldMetaData("id", QFieldType.LONG),
|
||||||
|
new QFieldMetaData("jobName", QFieldType.STRING),
|
||||||
|
new QFieldMetaData("jobGroup", QFieldType.STRING),
|
||||||
|
new QFieldMetaData("description", QFieldType.STRING)))
|
||||||
|
.getProcessMetaData()
|
||||||
|
.withPermissionRules(new QPermissionRules().withPermissionBaseName(getClass().getSimpleName()));
|
||||||
|
|
||||||
return StreamedETLWithFrontendProcess.processMetaDataBuilder()
|
MetaDataProducerMultiOutput output = new MetaDataProducerMultiOutput();
|
||||||
.withName(getClass().getSimpleName())
|
output.add(processMaker.apply("quartzJobDetails", "Pause Quartz Jobs"));
|
||||||
.withLabel("Pause Quartz Jobs")
|
output.add(processMaker.apply("quartzTriggers", "Pause Quartz Triggers").withName(getClass().getSimpleName() + "ForTriggers"));
|
||||||
.withPreviewMessage("This is a preview of the jobs that will be paused.")
|
return (output);
|
||||||
.withTableName(tableName)
|
|
||||||
.withSourceTable(tableName)
|
|
||||||
.withDestinationTable(tableName)
|
|
||||||
.withExtractStepClass(ExtractViaQueryStep.class)
|
|
||||||
.withTransformStepClass(NoopTransformStep.class)
|
|
||||||
.withLoadStepClass(getClass())
|
|
||||||
.withIcon(new QIcon("pause_circle_outline"))
|
|
||||||
.withReviewStepRecordFields(List.of(
|
|
||||||
new QFieldMetaData("id", QFieldType.LONG),
|
|
||||||
new QFieldMetaData("jobName", QFieldType.STRING),
|
|
||||||
new QFieldMetaData("jobGroup", QFieldType.STRING)))
|
|
||||||
.getProcessMetaData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,15 +23,18 @@ package com.kingsrook.qqq.backend.core.scheduler.quartz.processes;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
|
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||||
@ -43,33 +46,39 @@ import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class ResumeQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<QProcessMetaData>
|
public class ResumeQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<MetaDataProducerMultiOutput>
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public QProcessMetaData produce(QInstance qInstance) throws QException
|
public MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException
|
||||||
{
|
{
|
||||||
String tableName = "quartzJobDetails";
|
BiFunction<String, String, QProcessMetaData> processMaker = (String tableName, String label) ->
|
||||||
|
StreamedETLWithFrontendProcess.processMetaDataBuilder()
|
||||||
|
.withName(getClass().getSimpleName())
|
||||||
|
.withLabel(label)
|
||||||
|
.withPreviewMessage("This is a preview of the jobs that will be resumed.")
|
||||||
|
.withTableName(tableName)
|
||||||
|
.withSourceTable(tableName)
|
||||||
|
.withDestinationTable(tableName)
|
||||||
|
.withExtractStepClass(ExtractViaQueryStep.class)
|
||||||
|
.withTransformStepClass(NoopTransformStep.class)
|
||||||
|
.withLoadStepClass(getClass())
|
||||||
|
.withIcon(new QIcon("play_circle_outline"))
|
||||||
|
.withReviewStepRecordFields(List.of(
|
||||||
|
new QFieldMetaData("id", QFieldType.LONG),
|
||||||
|
new QFieldMetaData("jobName", QFieldType.STRING),
|
||||||
|
new QFieldMetaData("jobGroup", QFieldType.STRING),
|
||||||
|
new QFieldMetaData("description", QFieldType.STRING)))
|
||||||
|
.getProcessMetaData()
|
||||||
|
.withPermissionRules(new QPermissionRules().withPermissionBaseName(getClass().getSimpleName()));
|
||||||
|
|
||||||
return StreamedETLWithFrontendProcess.processMetaDataBuilder()
|
MetaDataProducerMultiOutput output = new MetaDataProducerMultiOutput();
|
||||||
.withName(getClass().getSimpleName())
|
output.add(processMaker.apply("quartzJobDetails", "Resume Quartz Jobs"));
|
||||||
.withLabel("Resume Quartz Jobs")
|
output.add(processMaker.apply("quartzTriggers", "Resume Quartz Triggers").withName(getClass().getSimpleName() + "ForTriggers"));
|
||||||
.withPreviewMessage("This is a preview of the jobs that will be resumed.")
|
return (output);
|
||||||
.withTableName(tableName)
|
|
||||||
.withSourceTable(tableName)
|
|
||||||
.withDestinationTable(tableName)
|
|
||||||
.withExtractStepClass(ExtractViaQueryStep.class)
|
|
||||||
.withTransformStepClass(NoopTransformStep.class)
|
|
||||||
.withLoadStepClass(getClass())
|
|
||||||
.withIcon(new QIcon("play_circle_outline"))
|
|
||||||
.withReviewStepRecordFields(List.of(
|
|
||||||
new QFieldMetaData("id", QFieldType.LONG),
|
|
||||||
new QFieldMetaData("jobName", QFieldType.STRING),
|
|
||||||
new QFieldMetaData("jobGroup", QFieldType.STRING)))
|
|
||||||
.getProcessMetaData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.model.session;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.Auth0AuthenticationModule;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.FullyAnonymousAuthenticationModule;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.MockAuthenticationModule;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.TableBasedAuthenticationModule;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for QSystemUserSession
|
||||||
|
*******************************************************************************/
|
||||||
|
class QSystemUserSessionTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
QSystemUserSession systemUserSession = new QSystemUserSession();
|
||||||
|
|
||||||
|
assertEquals(List.of(true), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
|
||||||
|
|
||||||
|
assertTrue(new Auth0AuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
|
||||||
|
assertTrue(new TableBasedAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
|
||||||
|
assertTrue(new MockAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
|
||||||
|
assertTrue(new FullyAnonymousAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
|
||||||
|
|
||||||
|
assertTrue(systemUserSession.hasPermission(null));
|
||||||
|
assertTrue(systemUserSession.hasPermission(""));
|
||||||
|
assertTrue(systemUserSession.hasPermission("anything"));
|
||||||
|
assertTrue(systemUserSession.hasPermission(UUID.randomUUID().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testWeDoNotBlowUpIfInstanceIsntInContextWhenPrimingAllAccessKeyNames()
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
QSystemUserSession.unsetAllAccessKeyNames();
|
||||||
|
|
||||||
|
QContext.clear();
|
||||||
|
QSystemUserSession systemUserSession = new QSystemUserSession();
|
||||||
|
assertEquals(Collections.emptyList(), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
|
||||||
|
|
||||||
|
QContext.setQInstance(qInstance);
|
||||||
|
systemUserSession = new QSystemUserSession();
|
||||||
|
assertEquals(List.of(true), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -50,11 +50,6 @@
|
|||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>8.0.30</version>
|
<version>8.0.30</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.mchange</groupId>
|
|
||||||
<artifactId>c3p0</artifactId>
|
|
||||||
<version>0.10.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
|
@ -47,18 +47,14 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
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.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.QueryJoin;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.AbstractFilterExpression;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.AbstractFilterExpression;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -139,34 +135,11 @@ public abstract class AbstractRDBMSAction
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Get a database connection, per the backend in the request.
|
** Get a database connection, per the backend in the request.
|
||||||
**
|
|
||||||
** Note that it may be a connection to a read-only backend, per query-hints,
|
|
||||||
** and backend settings.
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Connection getConnection(AbstractTableActionInput tableActionInput) throws SQLException
|
public static Connection getConnection(AbstractTableActionInput qTableRequest) throws SQLException
|
||||||
{
|
{
|
||||||
RDBMSBackendMetaData backend = (RDBMSBackendMetaData) tableActionInput.getBackend();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
|
return connectionManager.getConnection((RDBMSBackendMetaData) qTableRequest.getBackend());
|
||||||
boolean useReadOnly = false;
|
|
||||||
if(tableActionInput instanceof QueryInput queryInput)
|
|
||||||
{
|
|
||||||
useReadOnly = queryInput.hasQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
}
|
|
||||||
else if(tableActionInput instanceof CountInput countInput)
|
|
||||||
{
|
|
||||||
useReadOnly = countInput.hasQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
}
|
|
||||||
else if(tableActionInput instanceof AggregateInput aggregateInput)
|
|
||||||
{
|
|
||||||
useReadOnly = aggregateInput.hasQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(useReadOnly && backend.getReadOnlyBackendMetaData() != null)
|
|
||||||
{
|
|
||||||
return ConnectionManager.getConnection(backend.getReadOnlyBackendMetaData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConnectionManager.getConnection(backend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -54,12 +53,9 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public InsertOutput execute(InsertInput insertInput) throws QException
|
public InsertOutput execute(InsertInput insertInput) throws QException
|
||||||
{
|
{
|
||||||
InsertOutput rs = new InsertOutput();
|
InsertOutput rs = new InsertOutput();
|
||||||
QTableMetaData table = insertInput.getTable();
|
QTableMetaData table = insertInput.getTable();
|
||||||
|
|
||||||
Connection connection = null;
|
|
||||||
boolean needToCloseConnection = false;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
List<QFieldMetaData> insertableFields = table.getFields().values().stream()
|
List<QFieldMetaData> insertableFields = table.getFields().values().stream()
|
||||||
@ -76,6 +72,8 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
|||||||
List<QRecord> outputRecords = new ArrayList<>();
|
List<QRecord> outputRecords = new ArrayList<>();
|
||||||
rs.setRecords(outputRecords);
|
rs.setRecords(outputRecords);
|
||||||
|
|
||||||
|
Connection connection;
|
||||||
|
boolean needToCloseConnection = false;
|
||||||
if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
|
if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
|
||||||
{
|
{
|
||||||
connection = rdbmsTransaction.getConnection();
|
connection = rdbmsTransaction.getConnection();
|
||||||
@ -86,77 +84,87 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
|||||||
needToCloseConnection = true;
|
needToCloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE))
|
try
|
||||||
{
|
{
|
||||||
String tableName = escapeIdentifier(getTableName(table));
|
for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE))
|
||||||
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
|
|
||||||
List<Object> params = new ArrayList<>();
|
|
||||||
int recordIndex = 0;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// for each record in the page: //
|
|
||||||
// - if it has errors, skip it //
|
|
||||||
// - else add a "(?,?,...,?)," clause to the INSERT //
|
|
||||||
// - then add all fields into the params list //
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
for(QRecord record : page)
|
|
||||||
{
|
{
|
||||||
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
|
String tableName = escapeIdentifier(getTableName(table));
|
||||||
|
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
|
||||||
|
List<Object> params = new ArrayList<>();
|
||||||
|
int recordIndex = 0;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// for each record in the page: //
|
||||||
|
// - if it has errors, skip it //
|
||||||
|
// - else add a "(?,?,...,?)," clause to the INSERT //
|
||||||
|
// - then add all fields into the params list //
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
for(QRecord record : page)
|
||||||
{
|
{
|
||||||
|
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(recordIndex++ > 0)
|
||||||
|
{
|
||||||
|
sql.append(",");
|
||||||
|
}
|
||||||
|
sql.append("(").append(questionMarks).append(")");
|
||||||
|
|
||||||
|
for(QFieldMetaData field : insertableFields)
|
||||||
|
{
|
||||||
|
Serializable value = record.getValue(field.getName());
|
||||||
|
value = scrubValue(field, value);
|
||||||
|
params.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if all records had errors, copy them to the output, and continue w/o running query //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(recordIndex == 0)
|
||||||
|
{
|
||||||
|
for(QRecord record : page)
|
||||||
|
{
|
||||||
|
QRecord outputRecord = new QRecord(record);
|
||||||
|
outputRecords.add(outputRecord);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(recordIndex++ > 0)
|
Long mark = System.currentTimeMillis();
|
||||||
{
|
|
||||||
sql.append(",");
|
|
||||||
}
|
|
||||||
sql.append("(").append(questionMarks).append(")");
|
|
||||||
|
|
||||||
for(QFieldMetaData field : insertableFields)
|
///////////////////////////////////////////////////////////
|
||||||
{
|
// execute the insert, then foreach record in the input, //
|
||||||
Serializable value = record.getValue(field.getName());
|
// add it to the output, and set its generated id too. //
|
||||||
value = scrubValue(field, value);
|
///////////////////////////////////////////////////////////
|
||||||
params.add(value);
|
// todo sql customization - can edit sql and/or param list
|
||||||
}
|
// todo - non-serial-id style tables
|
||||||
}
|
// todo - other generated values, e.g., createDate... maybe need to re-select?
|
||||||
|
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
int index = 0;
|
||||||
// if all records had errors, copy them to the output, and continue w/o running query //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(recordIndex == 0)
|
|
||||||
{
|
|
||||||
for(QRecord record : page)
|
for(QRecord record : page)
|
||||||
{
|
{
|
||||||
QRecord outputRecord = new QRecord(record);
|
QRecord outputRecord = new QRecord(record);
|
||||||
outputRecords.add(outputRecord);
|
outputRecords.add(outputRecord);
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
|
||||||
|
{
|
||||||
|
Integer id = idList.get(index++);
|
||||||
|
outputRecord.setValue(table.getPrimaryKeyField(), id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
|
logSQL(sql, params, mark);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Long mark = System.currentTimeMillis();
|
finally
|
||||||
|
{
|
||||||
///////////////////////////////////////////////////////////
|
if(needToCloseConnection)
|
||||||
// execute the insert, then foreach record in the input, //
|
|
||||||
// add it to the output, and set its generated id too. //
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// todo sql customization - can edit sql and/or param list
|
|
||||||
// todo - non-serial-id style tables
|
|
||||||
// todo - other generated values, e.g., createDate... maybe need to re-select?
|
|
||||||
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
|
|
||||||
int index = 0;
|
|
||||||
for(QRecord record : page)
|
|
||||||
{
|
{
|
||||||
QRecord outputRecord = new QRecord(record);
|
connection.close();
|
||||||
outputRecords.add(outputRecord);
|
|
||||||
|
|
||||||
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
|
|
||||||
{
|
|
||||||
Integer id = idList.get(index++);
|
|
||||||
outputRecord.setValue(table.getPrimaryKeyField(), id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logSQL(sql, params, mark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rs;
|
return rs;
|
||||||
@ -165,21 +173,6 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
|||||||
{
|
{
|
||||||
throw new QException("Error executing insert: " + e.getMessage(), e);
|
throw new QException("Error executing insert: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
if(needToCloseConnection && connection != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
catch(SQLException se)
|
|
||||||
{
|
|
||||||
LOG.error("Error closing database connection", se);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
@ -81,6 +80,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -361,7 +361,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if we're allowed to use the mysqlResultSetOptimization, and we have the query hint of "expected large result set", then do it. //
|
// if we're allowed to use the mysqlResultSetOptimization, and we have the query hint of "expected large result set", then do it. //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(mysqlResultSetOptimizationEnabled && queryInput.getQueryHints() != null && queryInput.getQueryHints().contains(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS))
|
if(mysqlResultSetOptimizationEnabled && queryInput.getQueryHints() != null && queryInput.getQueryHints().contains(QueryInput.QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS))
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// mysql "optimization", presumably here - from Result Set section of https://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html //
|
// mysql "optimization", presumably here - from Result Set section of https://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html //
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.jdbc;
|
|
||||||
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.ConnectionPoolSettings;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
|
||||||
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import static com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager.getJdbcUrl;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public class C3P0PooledConnectionProvider implements ConnectionProviderInterface
|
|
||||||
{
|
|
||||||
private RDBMSBackendMetaData backend;
|
|
||||||
private ComboPooledDataSource connectionPool;
|
|
||||||
|
|
||||||
private long usageCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public void init(RDBMSBackendMetaData backend) throws QException
|
|
||||||
{
|
|
||||||
this.backend = backend;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ComboPooledDataSource pool = new ComboPooledDataSource();
|
|
||||||
pool.setDriverClass(ConnectionManager.getJdbcDriverClassName(backend));
|
|
||||||
pool.setJdbcUrl(getJdbcUrl(backend));
|
|
||||||
pool.setUser(backend.getUsername());
|
|
||||||
pool.setPassword(backend.getPassword());
|
|
||||||
|
|
||||||
ConnectionPoolSettings poolSettings = backend.getConnectionPoolSettings();
|
|
||||||
if(poolSettings != null)
|
|
||||||
{
|
|
||||||
if(poolSettings.getInitialPoolSize() != null)
|
|
||||||
{
|
|
||||||
pool.setInitialPoolSize(poolSettings.getInitialPoolSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getMinPoolSize() != null)
|
|
||||||
{
|
|
||||||
pool.setMinPoolSize(poolSettings.getMinPoolSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getMaxPoolSize() != null)
|
|
||||||
{
|
|
||||||
pool.setMaxPoolSize(poolSettings.getMaxPoolSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getAcquireIncrement() != null)
|
|
||||||
{
|
|
||||||
pool.setAcquireIncrement(poolSettings.getAcquireIncrement());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getMaxConnectionAgeSeconds() != null)
|
|
||||||
{
|
|
||||||
pool.setMaxConnectionAge(poolSettings.getMaxConnectionAgeSeconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getMaxIdleTimeSeconds() != null)
|
|
||||||
{
|
|
||||||
pool.setMaxIdleTime(poolSettings.getMaxIdleTimeSeconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getMaxIdleTimeExcessConnectionsSeconds() != null)
|
|
||||||
{
|
|
||||||
pool.setMaxIdleTimeExcessConnections(poolSettings.getMaxIdleTimeExcessConnectionsSeconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getCheckoutTimeoutSeconds() != null)
|
|
||||||
{
|
|
||||||
pool.setCheckoutTimeout(poolSettings.getCheckoutTimeoutSeconds() * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(poolSettings.getTestConnectionOnCheckout() != null)
|
|
||||||
{
|
|
||||||
pool.setTestConnectionOnCheckout(poolSettings.getTestConnectionOnCheckout());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customizePool(pool);
|
|
||||||
|
|
||||||
this.connectionPool = pool;
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
throw (new QException("Error Initializing C3P0PooledConnectionProvider for backend [" + backend.getName() + "]", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
protected void customizePool(ComboPooledDataSource pool)
|
|
||||||
{
|
|
||||||
////////////////////////
|
|
||||||
// noop in base class //
|
|
||||||
////////////////////////
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException
|
|
||||||
{
|
|
||||||
usageCount++;
|
|
||||||
return (this.connectionPool.getConnection());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public JSONObject dumpDebug() throws SQLException
|
|
||||||
{
|
|
||||||
JSONObject rs = new JSONObject(new LinkedHashMap<>());
|
|
||||||
|
|
||||||
JSONObject settings = new JSONObject(new LinkedHashMap<>());
|
|
||||||
rs.put("settings", settings);
|
|
||||||
settings.put("initialPoolSize", connectionPool.getInitialPoolSize());
|
|
||||||
settings.put("minPoolSize", connectionPool.getMinPoolSize());
|
|
||||||
settings.put("maxPoolSize", connectionPool.getMaxPoolSize());
|
|
||||||
settings.put("acquireIncrement", connectionPool.getAcquireIncrement());
|
|
||||||
settings.put("maxConnectionAge", connectionPool.getMaxConnectionAge());
|
|
||||||
settings.put("maxIdleTime", connectionPool.getMaxIdleTime());
|
|
||||||
settings.put("maxIdleTimeExcessConnections", connectionPool.getMaxIdleTimeExcessConnections());
|
|
||||||
settings.put("checkoutTimeout", connectionPool.getCheckoutTimeout());
|
|
||||||
settings.put("testConnectionOnCheckout", connectionPool.isTestConnectionOnCheckout());
|
|
||||||
|
|
||||||
JSONObject state = new JSONObject(new LinkedHashMap<>());
|
|
||||||
rs.put("state", state);
|
|
||||||
state.put("numUsages", usageCount);
|
|
||||||
state.put("numConnections", connectionPool.getNumConnections());
|
|
||||||
state.put("numBusyConnections", connectionPool.getNumBusyConnections());
|
|
||||||
state.put("numIdleConnections", connectionPool.getNumIdleConnections());
|
|
||||||
state.put("numFailedCheckins", connectionPool.getNumFailedCheckinsDefaultUser());
|
|
||||||
state.put("numFailedCheckouts", connectionPool.getNumFailedCheckoutsDefaultUser());
|
|
||||||
state.put("numFailedIdleTests", connectionPool.getNumFailedIdleTestsDefaultUser());
|
|
||||||
state.put("numThreadsAwaitingCheckout", connectionPool.getNumThreadsAwaitingCheckoutDefaultUser());
|
|
||||||
return (rs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -23,110 +23,25 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc;
|
|||||||
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Class to manage access to JDBC Connections.
|
|
||||||
**
|
**
|
||||||
** Relies heavily on RDBMSBackendMetaData.
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class ConnectionManager
|
public class ConnectionManager
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(ConnectionManager.class);
|
|
||||||
|
|
||||||
private static final Map<String, ConnectionProviderInterface> connectionProviderMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Connection getConnection(RDBMSBackendMetaData backend) throws SQLException
|
public Connection getConnection(RDBMSBackendMetaData backend) throws SQLException
|
||||||
{
|
{
|
||||||
try
|
String jdbcURL = getJdbcUrl(backend);
|
||||||
{
|
return DriverManager.getConnection(jdbcURL, backend.getUsername(), backend.getPassword());
|
||||||
ConnectionProviderInterface connectionProvider = getConnectionProvider(backend);
|
|
||||||
return connectionProvider.getConnection();
|
|
||||||
}
|
|
||||||
catch(QException qe)
|
|
||||||
{
|
|
||||||
throw (new SQLException("Error getting connection", qe));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static ConnectionProviderInterface getConnectionProvider(RDBMSBackendMetaData backend) throws QException
|
|
||||||
{
|
|
||||||
if(!connectionProviderMap.containsKey(backend.getName()))
|
|
||||||
{
|
|
||||||
synchronized(connectionProviderMap)
|
|
||||||
{
|
|
||||||
if(!connectionProviderMap.containsKey(backend.getName()))
|
|
||||||
{
|
|
||||||
QCodeReference connectionProviderReference = backend.getConnectionProvider();
|
|
||||||
boolean usingDefaultSimpleProvider = false;
|
|
||||||
if(connectionProviderReference == null)
|
|
||||||
{
|
|
||||||
connectionProviderReference = new QCodeReference(SimpleConnectionProvider.class);
|
|
||||||
usingDefaultSimpleProvider = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info("Initializing connection provider for RDBMS backend", logPair("backendName", backend.getName()), logPair("connectionProvider", connectionProviderReference.getName()), logPair("usingDefaultSimpleProvider", usingDefaultSimpleProvider));
|
|
||||||
ConnectionProviderInterface connectionProvider = QCodeLoader.getAdHoc(ConnectionProviderInterface.class, connectionProviderReference);
|
|
||||||
connectionProvider.init(backend);
|
|
||||||
|
|
||||||
connectionProviderMap.put(backend.getName(), connectionProvider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (connectionProviderMap.get(backend.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public static JSONArray dumpConnectionProviderDebug()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
JSONArray rs = new JSONArray();
|
|
||||||
for(Map.Entry<String, ConnectionProviderInterface> entry : connectionProviderMap.entrySet())
|
|
||||||
{
|
|
||||||
JSONObject jsonObject = new JSONObject(new LinkedHashMap<>());
|
|
||||||
jsonObject.put("backendName", entry.getKey());
|
|
||||||
jsonObject.put("connectionProviderClass", entry.getValue().getClass().getName());
|
|
||||||
jsonObject.put("values", entry.getValue().dumpDebug());
|
|
||||||
rs.put(jsonObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (rs);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
String message = "Error dumping debug data for connection providers";
|
|
||||||
LOG.warn(message, e);
|
|
||||||
return (new JSONArray(new JSONObject(Map.of("error", e.getMessage()))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -172,14 +87,4 @@ public class ConnectionManager
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** reset the map of connection providers - not necessarily meant to be useful
|
|
||||||
** in production code - written for use in qqq tests.
|
|
||||||
*******************************************************************************/
|
|
||||||
static void resetConnectionProviders()
|
|
||||||
{
|
|
||||||
connectionProviderMap.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.jdbc;
|
|
||||||
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** interface for classes that can provide jdbc Connections for an RDBMS backend.
|
|
||||||
*******************************************************************************/
|
|
||||||
public interface ConnectionProviderInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
void init(RDBMSBackendMetaData backend) throws QException;
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
Connection getConnection() throws SQLException;
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
default JSONObject dumpDebug() throws SQLException
|
|
||||||
{
|
|
||||||
JSONObject rs = new JSONObject();
|
|
||||||
rs.put("nothingToReport", true);
|
|
||||||
return (rs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.jdbc;
|
|
||||||
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.DriverManager;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
|
||||||
import static com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager.getJdbcUrl;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Simple connection provider - no pooling, just opens a new connection for
|
|
||||||
** every request.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class SimpleConnectionProvider implements ConnectionProviderInterface
|
|
||||||
{
|
|
||||||
private RDBMSBackendMetaData backend;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public void init(RDBMSBackendMetaData backend)
|
|
||||||
{
|
|
||||||
this.backend = backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException
|
|
||||||
{
|
|
||||||
String jdbcURL = getJdbcUrl(backend);
|
|
||||||
return DriverManager.getConnection(jdbcURL, backend.getUsername(), backend.getPassword());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,326 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.model.metadata;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Settings for a connection pool (if your backend is configured to use one).
|
|
||||||
** Originally based on the most common settings for C3P0 - see
|
|
||||||
** https://www.mchange.com/projects/c3p0/#configuration
|
|
||||||
**
|
|
||||||
** If you want more - you'll be looking at defining your own subclass of
|
|
||||||
** C3P0PooledConnectionProvider and possibly this class.
|
|
||||||
**
|
|
||||||
** If using a pool other than C3P0 - some of these may apply others may not.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class ConnectionPoolSettings
|
|
||||||
{
|
|
||||||
private Integer initialPoolSize;
|
|
||||||
private Integer minPoolSize;
|
|
||||||
private Integer maxPoolSize;
|
|
||||||
private Integer acquireIncrement;
|
|
||||||
private Integer maxConnectionAgeSeconds;
|
|
||||||
private Integer maxIdleTimeSeconds;
|
|
||||||
private Integer maxIdleTimeExcessConnectionsSeconds;
|
|
||||||
private Integer checkoutTimeoutSeconds;
|
|
||||||
private Boolean testConnectionOnCheckout;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for initialPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getInitialPoolSize()
|
|
||||||
{
|
|
||||||
return (this.initialPoolSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for initialPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setInitialPoolSize(Integer initialPoolSize)
|
|
||||||
{
|
|
||||||
this.initialPoolSize = initialPoolSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for initialPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withInitialPoolSize(Integer initialPoolSize)
|
|
||||||
{
|
|
||||||
this.initialPoolSize = initialPoolSize;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for minPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getMinPoolSize()
|
|
||||||
{
|
|
||||||
return (this.minPoolSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for minPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMinPoolSize(Integer minPoolSize)
|
|
||||||
{
|
|
||||||
this.minPoolSize = minPoolSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for minPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withMinPoolSize(Integer minPoolSize)
|
|
||||||
{
|
|
||||||
this.minPoolSize = minPoolSize;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for maxPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getMaxPoolSize()
|
|
||||||
{
|
|
||||||
return (this.maxPoolSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for maxPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMaxPoolSize(Integer maxPoolSize)
|
|
||||||
{
|
|
||||||
this.maxPoolSize = maxPoolSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for maxPoolSize
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withMaxPoolSize(Integer maxPoolSize)
|
|
||||||
{
|
|
||||||
this.maxPoolSize = maxPoolSize;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for acquireIncrement
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getAcquireIncrement()
|
|
||||||
{
|
|
||||||
return (this.acquireIncrement);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for acquireIncrement
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setAcquireIncrement(Integer acquireIncrement)
|
|
||||||
{
|
|
||||||
this.acquireIncrement = acquireIncrement;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for acquireIncrement
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withAcquireIncrement(Integer acquireIncrement)
|
|
||||||
{
|
|
||||||
this.acquireIncrement = acquireIncrement;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for maxConnectionAgeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getMaxConnectionAgeSeconds()
|
|
||||||
{
|
|
||||||
return (this.maxConnectionAgeSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for maxConnectionAgeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMaxConnectionAgeSeconds(Integer maxConnectionAgeSeconds)
|
|
||||||
{
|
|
||||||
this.maxConnectionAgeSeconds = maxConnectionAgeSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for maxConnectionAgeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withMaxConnectionAgeSeconds(Integer maxConnectionAgeSeconds)
|
|
||||||
{
|
|
||||||
this.maxConnectionAgeSeconds = maxConnectionAgeSeconds;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for maxIdleTimeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getMaxIdleTimeSeconds()
|
|
||||||
{
|
|
||||||
return (this.maxIdleTimeSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for maxIdleTimeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMaxIdleTimeSeconds(Integer maxIdleTimeSeconds)
|
|
||||||
{
|
|
||||||
this.maxIdleTimeSeconds = maxIdleTimeSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for maxIdleTimeSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withMaxIdleTimeSeconds(Integer maxIdleTimeSeconds)
|
|
||||||
{
|
|
||||||
this.maxIdleTimeSeconds = maxIdleTimeSeconds;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for maxIdleTimeExcessConnectionsSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getMaxIdleTimeExcessConnectionsSeconds()
|
|
||||||
{
|
|
||||||
return (this.maxIdleTimeExcessConnectionsSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for maxIdleTimeExcessConnectionsSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMaxIdleTimeExcessConnectionsSeconds(Integer maxIdleTimeExcessConnectionsSeconds)
|
|
||||||
{
|
|
||||||
this.maxIdleTimeExcessConnectionsSeconds = maxIdleTimeExcessConnectionsSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for maxIdleTimeExcessConnectionsSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withMaxIdleTimeExcessConnectionsSeconds(Integer maxIdleTimeExcessConnectionsSeconds)
|
|
||||||
{
|
|
||||||
this.maxIdleTimeExcessConnectionsSeconds = maxIdleTimeExcessConnectionsSeconds;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for testConnectionOnCheckout
|
|
||||||
*******************************************************************************/
|
|
||||||
public Boolean getTestConnectionOnCheckout()
|
|
||||||
{
|
|
||||||
return (this.testConnectionOnCheckout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for testConnectionOnCheckout
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setTestConnectionOnCheckout(Boolean testConnectionOnCheckout)
|
|
||||||
{
|
|
||||||
this.testConnectionOnCheckout = testConnectionOnCheckout;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for testConnectionOnCheckout
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withTestConnectionOnCheckout(Boolean testConnectionOnCheckout)
|
|
||||||
{
|
|
||||||
this.testConnectionOnCheckout = testConnectionOnCheckout;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for checkoutTimeoutSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public Integer getCheckoutTimeoutSeconds()
|
|
||||||
{
|
|
||||||
return (this.checkoutTimeoutSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for checkoutTimeoutSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setCheckoutTimeoutSeconds(Integer checkoutTimeoutSeconds)
|
|
||||||
{
|
|
||||||
this.checkoutTimeoutSeconds = checkoutTimeoutSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for checkoutTimeoutSeconds
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings withCheckoutTimeoutSeconds(Integer checkoutTimeoutSeconds)
|
|
||||||
{
|
|
||||||
this.checkoutTimeoutSeconds = checkoutTimeoutSeconds;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.module.rdbms.model.metadata;
|
|||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule;
|
import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule;
|
||||||
|
|
||||||
|
|
||||||
@ -43,12 +42,6 @@ public class RDBMSBackendMetaData extends QBackendMetaData
|
|||||||
private String jdbcUrl;
|
private String jdbcUrl;
|
||||||
private String jdbcDriverClassName;
|
private String jdbcDriverClassName;
|
||||||
|
|
||||||
private QCodeReference connectionProvider;
|
|
||||||
|
|
||||||
private ConnectionPoolSettings connectionPoolSettings;
|
|
||||||
|
|
||||||
private RDBMSBackendMetaData readOnlyBackendMetaData;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -323,7 +316,6 @@ public class RDBMSBackendMetaData extends QBackendMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for jdbcDriverClassName
|
** Getter for jdbcDriverClassName
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -354,96 +346,4 @@ public class RDBMSBackendMetaData extends QBackendMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for connectionProvider
|
|
||||||
*******************************************************************************/
|
|
||||||
public QCodeReference getConnectionProvider()
|
|
||||||
{
|
|
||||||
return (this.connectionProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for connectionProvider
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setConnectionProvider(QCodeReference connectionProvider)
|
|
||||||
{
|
|
||||||
this.connectionProvider = connectionProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for connectionProvider
|
|
||||||
*******************************************************************************/
|
|
||||||
public RDBMSBackendMetaData withConnectionProvider(QCodeReference connectionProvider)
|
|
||||||
{
|
|
||||||
this.connectionProvider = connectionProvider;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for readOnlyBackendMetaData
|
|
||||||
*******************************************************************************/
|
|
||||||
public RDBMSBackendMetaData getReadOnlyBackendMetaData()
|
|
||||||
{
|
|
||||||
return (this.readOnlyBackendMetaData);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for readOnlyBackendMetaData
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setReadOnlyBackendMetaData(RDBMSBackendMetaData readOnlyBackendMetaData)
|
|
||||||
{
|
|
||||||
this.readOnlyBackendMetaData = readOnlyBackendMetaData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for readOnlyBackendMetaData
|
|
||||||
*******************************************************************************/
|
|
||||||
public RDBMSBackendMetaData withReadOnlyBackendMetaData(RDBMSBackendMetaData readOnlyBackendMetaData)
|
|
||||||
{
|
|
||||||
this.readOnlyBackendMetaData = readOnlyBackendMetaData;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for connectionPoolSettings
|
|
||||||
*******************************************************************************/
|
|
||||||
public ConnectionPoolSettings getConnectionPoolSettings()
|
|
||||||
{
|
|
||||||
return (this.connectionPoolSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for connectionPoolSettings
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setConnectionPoolSettings(ConnectionPoolSettings connectionPoolSettings)
|
|
||||||
{
|
|
||||||
this.connectionPoolSettings = connectionPoolSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for connectionPoolSettings
|
|
||||||
*******************************************************************************/
|
|
||||||
public RDBMSBackendMetaData withConnectionPoolSettings(ConnectionPoolSettings connectionPoolSettings)
|
|
||||||
{
|
|
||||||
this.connectionPoolSettings = connectionPoolSettings;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,5 @@ public class RDBMSActionTest extends BaseTest
|
|||||||
ConnectionManager connectionManager = new ConnectionManager();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||||
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
||||||
connection.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,237 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. 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.jdbc;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.BaseTest;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.ConnectionPoolSettings;
|
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
|
||||||
import com.mchange.v2.resourcepool.TimeoutException;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
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.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Unit test for C3P0PooledConnectionProvider
|
|
||||||
*******************************************************************************/
|
|
||||||
class C3P0PooledConnectionProviderTest extends BaseTest
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@BeforeEach
|
|
||||||
void beforeEach() throws Exception
|
|
||||||
{
|
|
||||||
TestUtils.primeTestDatabase("prime-test-database.sql");
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// must call this after the primeTestDatabase call (as i uses a raw version of the backend, w/o our updated settings) //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ConnectionManager.resetConnectionProviders();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@AfterEach
|
|
||||||
void afterEach()
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// just for good measure, do this after each test in here //
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
ConnectionManager.resetConnectionProviders();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
// @RepeatedTest(100)
|
|
||||||
void test() throws Exception
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// change the default database backend to use the class under test here - the C3PL connection pool provider //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
QInstance qInstance = TestUtils.defineInstance();
|
|
||||||
RDBMSBackendMetaData backend = (RDBMSBackendMetaData) qInstance.getBackend(TestUtils.DEFAULT_BACKEND_NAME);
|
|
||||||
backend.setConnectionProvider(new QCodeReference(C3P0PooledConnectionProvider.class));
|
|
||||||
QContext.init(qInstance, new QSession());
|
|
||||||
|
|
||||||
for(int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
new QueryAction().execute(new QueryInput(TestUtils.TABLE_NAME_PERSON));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject debugValues = getDebugStateValues(true);
|
|
||||||
assertThat(debugValues.getInt("numConnections")).isEqualTo(3); // one time (in a @RepeatedTest(100) we saw a 3 != 6 here...)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// open up 4 transactions - confirm the pool opens some new conns //
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
List<QBackendTransaction> transactions = new ArrayList<>();
|
|
||||||
for(int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
transactions.add(QBackendTransaction.openFor(new InsertInput(TestUtils.TABLE_NAME_PERSON)));
|
|
||||||
}
|
|
||||||
|
|
||||||
debugValues = getDebugStateValues(true);
|
|
||||||
assertThat(debugValues.getInt("numConnections")).isGreaterThan(3);
|
|
||||||
|
|
||||||
transactions.forEach(transaction -> transaction.close());
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// might take a second for the pool to re-claim the closed connections //
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
boolean foundMatch = false;
|
|
||||||
for(int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
debugValues = getDebugStateValues(true);
|
|
||||||
if(debugValues.getInt("numConnections") == debugValues.getInt("numIdleConnections"))
|
|
||||||
{
|
|
||||||
foundMatch = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println("oops!");
|
|
||||||
SleepUtils.sleep(250, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(foundMatch, "The pool didn't re-claim all connections...");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
void testPoolSettings() throws Exception
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// change the default database backend to use the class under test here - the C3PL connection pool provider //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
QInstance qInstance = TestUtils.defineInstance();
|
|
||||||
RDBMSBackendMetaData backend = (RDBMSBackendMetaData) qInstance.getBackend(TestUtils.DEFAULT_BACKEND_NAME);
|
|
||||||
backend.setConnectionProvider(new QCodeReference(C3P0PooledConnectionProvider.class));
|
|
||||||
backend.setConnectionPoolSettings(new ConnectionPoolSettings()
|
|
||||||
.withInitialPoolSize(2)
|
|
||||||
.withAcquireIncrement(1)
|
|
||||||
.withMinPoolSize(1)
|
|
||||||
.withMaxPoolSize(4)
|
|
||||||
.withCheckoutTimeoutSeconds(1));
|
|
||||||
QContext.init(qInstance, new QSession());
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// assert initial size //
|
|
||||||
/////////////////////////
|
|
||||||
new QueryAction().execute(new QueryInput(TestUtils.TABLE_NAME_PERSON));
|
|
||||||
JSONObject debugValues = getDebugStateValues(true);
|
|
||||||
assertThat(debugValues.getInt("numConnections")).isEqualTo(2);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// open (and close) 5 conns - shouldn't get bigger than initial size //
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
for(int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
new QueryAction().execute(new QueryInput(TestUtils.TABLE_NAME_PERSON));
|
|
||||||
}
|
|
||||||
debugValues = getDebugStateValues(true);
|
|
||||||
assertThat(debugValues.getInt("numConnections")).isEqualTo(2); // one time (in a @RepeatedTest(100) we saw a 3 != 6 here...)
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// open up 4 transactions - confirm the pool opens some new conns, but stops at the max, and throws based on checkoutTimeout setting //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
List<QBackendTransaction> transactions = new ArrayList<>();
|
|
||||||
for(int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
if(i == 4)
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// expect this one to fail - full pool! //
|
|
||||||
//////////////////////////////////////////
|
|
||||||
assertThatThrownBy(() -> QBackendTransaction.openFor(new InsertInput(TestUtils.TABLE_NAME_PERSON)))
|
|
||||||
.hasRootCauseInstanceOf(TimeoutException.class);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
transactions.add(QBackendTransaction.openFor(new InsertInput(TestUtils.TABLE_NAME_PERSON)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugValues = getDebugStateValues(true);
|
|
||||||
assertThat(debugValues.getInt("numConnections")).isEqualTo(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static JSONObject getDebugStateValues(boolean printIt)
|
|
||||||
{
|
|
||||||
JSONArray debugArray = ConnectionManager.dumpConnectionProviderDebug();
|
|
||||||
for(int i = 0; i < debugArray.length(); i++)
|
|
||||||
{
|
|
||||||
JSONObject object = debugArray.getJSONObject(i);
|
|
||||||
if(TestUtils.DEFAULT_BACKEND_NAME.equals(object.optString("backendName")))
|
|
||||||
{
|
|
||||||
JSONObject values = object.getJSONObject("values");
|
|
||||||
if(printIt)
|
|
||||||
{
|
|
||||||
System.out.println(values.toString(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject state = values.getJSONObject("state");
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail("Didn't find debug values...");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -64,20 +64,18 @@ class QueryManagerTest extends BaseTest
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws SQLException
|
void beforeEach() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, """
|
||||||
QueryManager.executeUpdate(connection, """
|
CREATE TABLE test_table
|
||||||
CREATE TABLE test_table
|
(
|
||||||
(
|
int_col INTEGER,
|
||||||
int_col INTEGER,
|
datetime_col DATETIME,
|
||||||
datetime_col DATETIME,
|
char_col CHAR(1),
|
||||||
char_col CHAR(1),
|
date_col DATE,
|
||||||
date_col DATE,
|
time_col TIME,
|
||||||
time_col TIME,
|
long_col LONG
|
||||||
long_col LONG
|
)
|
||||||
)
|
""");
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -88,10 +86,8 @@ class QueryManagerTest extends BaseTest
|
|||||||
@AfterEach
|
@AfterEach
|
||||||
void afterEach() throws SQLException
|
void afterEach() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, "DROP TABLE test_table");
|
||||||
QueryManager.executeUpdate(connection, "DROP TABLE test_table");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,58 +109,56 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testBindParams() throws SQLException
|
void testBindParams() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
long ctMillis = System.currentTimeMillis();
|
||||||
|
Connection connection = getConnection();
|
||||||
|
PreparedStatement ps = connection.prepareStatement("UPDATE test_table SET int_col = ? WHERE int_col > 0");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// these calls - we just want to assert that they don't throw any exceptions //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
QueryManager.bindParamObject(ps, 1, (short) 1);
|
||||||
|
QueryManager.bindParamObject(ps, 1, (long) 1);
|
||||||
|
QueryManager.bindParamObject(ps, 1, true);
|
||||||
|
QueryManager.bindParamObject(ps, 1, BigDecimal.ONE);
|
||||||
|
QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
|
||||||
|
QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis));
|
||||||
|
QueryManager.bindParamObject(ps, 1, new Date(ctMillis));
|
||||||
|
QueryManager.bindParamObject(ps, 1, new GregorianCalendar());
|
||||||
|
QueryManager.bindParamObject(ps, 1, LocalDate.now());
|
||||||
|
QueryManager.bindParamObject(ps, 1, OffsetDateTime.now());
|
||||||
|
QueryManager.bindParamObject(ps, 1, LocalDateTime.now());
|
||||||
|
QueryManager.bindParamObject(ps, 1, AutomationStatus.PENDING_INSERT_AUTOMATIONS);
|
||||||
|
|
||||||
|
assertThrows(SQLException.class, () ->
|
||||||
{
|
{
|
||||||
long ctMillis = System.currentTimeMillis();
|
QueryManager.bindParamObject(ps, 1, new Object());
|
||||||
PreparedStatement ps = connection.prepareStatement("UPDATE test_table SET int_col = ? WHERE int_col > 0");
|
});
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
QueryManager.bindParam(ps, 1, (Integer) null);
|
||||||
// these calls - we just want to assert that they don't throw any exceptions //
|
QueryManager.bindParam(ps, 1, (Boolean) null);
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
QueryManager.bindParam(ps, 1, (BigDecimal) null);
|
||||||
QueryManager.bindParamObject(ps, 1, (short) 1);
|
QueryManager.bindParam(ps, 1, (byte[]) null);
|
||||||
QueryManager.bindParamObject(ps, 1, (long) 1);
|
QueryManager.bindParam(ps, 1, (Timestamp) null);
|
||||||
QueryManager.bindParamObject(ps, 1, true);
|
QueryManager.bindParam(ps, 1, (String) null);
|
||||||
QueryManager.bindParamObject(ps, 1, BigDecimal.ONE);
|
QueryManager.bindParam(ps, 1, (Date) null);
|
||||||
QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
|
QueryManager.bindParam(ps, 1, (GregorianCalendar) null);
|
||||||
QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis));
|
QueryManager.bindParam(ps, 1, (LocalDate) null);
|
||||||
QueryManager.bindParamObject(ps, 1, new Date(ctMillis));
|
QueryManager.bindParam(ps, 1, (LocalDateTime) null);
|
||||||
QueryManager.bindParamObject(ps, 1, new GregorianCalendar());
|
|
||||||
QueryManager.bindParamObject(ps, 1, LocalDate.now());
|
|
||||||
QueryManager.bindParamObject(ps, 1, OffsetDateTime.now());
|
|
||||||
QueryManager.bindParamObject(ps, 1, LocalDateTime.now());
|
|
||||||
QueryManager.bindParamObject(ps, 1, AutomationStatus.PENDING_INSERT_AUTOMATIONS);
|
|
||||||
|
|
||||||
assertThrows(SQLException.class, () ->
|
QueryManager.bindParam(ps, 1, 1);
|
||||||
{
|
QueryManager.bindParam(ps, 1, true);
|
||||||
QueryManager.bindParamObject(ps, 1, new Object());
|
QueryManager.bindParam(ps, 1, BigDecimal.ONE);
|
||||||
});
|
QueryManager.bindParam(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
|
||||||
|
QueryManager.bindParam(ps, 1, new Timestamp(ctMillis));
|
||||||
|
QueryManager.bindParam(ps, 1, "hello");
|
||||||
|
QueryManager.bindParam(ps, 1, new Date(ctMillis));
|
||||||
|
QueryManager.bindParam(ps, 1, new GregorianCalendar());
|
||||||
|
QueryManager.bindParam(ps, 1, LocalDate.now());
|
||||||
|
QueryManager.bindParam(ps, 1, LocalDateTime.now());
|
||||||
|
|
||||||
QueryManager.bindParam(ps, 1, (Integer) null);
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
QueryManager.bindParam(ps, 1, (Boolean) null);
|
// originally longs were being downgraded to int when binding, so, verify that doesn't happen //
|
||||||
QueryManager.bindParam(ps, 1, (BigDecimal) null);
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
QueryManager.bindParam(ps, 1, (byte[]) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (Timestamp) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (String) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (Date) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (GregorianCalendar) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (LocalDate) null);
|
|
||||||
QueryManager.bindParam(ps, 1, (LocalDateTime) null);
|
|
||||||
|
|
||||||
QueryManager.bindParam(ps, 1, 1);
|
|
||||||
QueryManager.bindParam(ps, 1, true);
|
|
||||||
QueryManager.bindParam(ps, 1, BigDecimal.ONE);
|
|
||||||
QueryManager.bindParam(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
|
|
||||||
QueryManager.bindParam(ps, 1, new Timestamp(ctMillis));
|
|
||||||
QueryManager.bindParam(ps, 1, "hello");
|
|
||||||
QueryManager.bindParam(ps, 1, new Date(ctMillis));
|
|
||||||
QueryManager.bindParam(ps, 1, new GregorianCalendar());
|
|
||||||
QueryManager.bindParam(ps, 1, LocalDate.now());
|
|
||||||
QueryManager.bindParam(ps, 1, LocalDateTime.now());
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// originally longs were being downgraded to int when binding, so, verify that doesn't happen //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -175,21 +169,19 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testLongBinding() throws SQLException
|
void testLongBinding() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Long biggerThanMaxInteger = 2147483648L;
|
||||||
{
|
|
||||||
Long biggerThanMaxInteger = 2147483648L;
|
|
||||||
|
|
||||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO test_table (long_col) VALUES (?)");
|
Connection connection = getConnection();
|
||||||
QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
|
PreparedStatement ps = connection.prepareStatement("INSERT INTO test_table (long_col) VALUES (?)");
|
||||||
ps.execute();
|
QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
|
||||||
|
ps.execute();
|
||||||
|
|
||||||
ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?");
|
ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?");
|
||||||
QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
|
QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
|
||||||
ps.execute();
|
ps.execute();
|
||||||
ResultSet rs = ps.getResultSet();
|
ResultSet rs = ps.getResultSet();
|
||||||
assertTrue(rs.next());
|
assertTrue(rs.next());
|
||||||
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
|
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -200,45 +192,43 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testGetValueMethods() throws SQLException
|
void testGetValueMethods() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Long biggerThanMaxInteger = 2147483648L;
|
||||||
{
|
|
||||||
Long biggerThanMaxInteger = 2147483648L;
|
|
||||||
|
|
||||||
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col, long_col) VALUES (1, now(), 'A', " + biggerThanMaxInteger + ")");
|
Connection connection = getConnection();
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT int_col, datetime_col, char_col, long_col from test_table");
|
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col, long_col) VALUES (1, now(), 'A', " + biggerThanMaxInteger + ")");
|
||||||
preparedStatement.execute();
|
PreparedStatement preparedStatement = connection.prepareStatement("SELECT int_col, datetime_col, char_col, long_col from test_table");
|
||||||
ResultSet rs = preparedStatement.getResultSet();
|
preparedStatement.execute();
|
||||||
rs.next();
|
ResultSet rs = preparedStatement.getResultSet();
|
||||||
|
rs.next();
|
||||||
|
|
||||||
assertEquals(1, QueryManager.getInteger(rs, "int_col"));
|
assertEquals(1, QueryManager.getInteger(rs, "int_col"));
|
||||||
assertEquals(1, QueryManager.getInteger(rs, 1));
|
assertEquals(1, QueryManager.getInteger(rs, 1));
|
||||||
assertEquals(1L, QueryManager.getLong(rs, "int_col"));
|
assertEquals(1L, QueryManager.getLong(rs, "int_col"));
|
||||||
assertEquals(1L, QueryManager.getLong(rs, 1));
|
assertEquals(1L, QueryManager.getLong(rs, 1));
|
||||||
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "int_col"));
|
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "int_col"));
|
||||||
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1));
|
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1));
|
||||||
assertEquals(1, QueryManager.getObject(rs, "int_col"));
|
assertEquals(1, QueryManager.getObject(rs, "int_col"));
|
||||||
assertEquals(1, QueryManager.getObject(rs, 1));
|
assertEquals(1, QueryManager.getObject(rs, 1));
|
||||||
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col"));
|
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col"));
|
||||||
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1));
|
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1));
|
||||||
assertEquals(true, QueryManager.getBoolean(rs, "int_col"));
|
assertEquals(true, QueryManager.getBoolean(rs, "int_col"));
|
||||||
assertEquals(true, QueryManager.getBoolean(rs, 1));
|
assertEquals(true, QueryManager.getBoolean(rs, 1));
|
||||||
assertNotNull(QueryManager.getDate(rs, "datetime_col"));
|
assertNotNull(QueryManager.getDate(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getDate(rs, 2));
|
assertNotNull(QueryManager.getDate(rs, 2));
|
||||||
assertNotNull(QueryManager.getCalendar(rs, "datetime_col"));
|
assertNotNull(QueryManager.getCalendar(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getCalendar(rs, 2));
|
assertNotNull(QueryManager.getCalendar(rs, 2));
|
||||||
assertNotNull(QueryManager.getLocalDate(rs, "datetime_col"));
|
assertNotNull(QueryManager.getLocalDate(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getLocalDate(rs, 2));
|
assertNotNull(QueryManager.getLocalDate(rs, 2));
|
||||||
assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
|
assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getLocalDateTime(rs, 2));
|
assertNotNull(QueryManager.getLocalDateTime(rs, 2));
|
||||||
assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
|
assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getOffsetDateTime(rs, 2));
|
assertNotNull(QueryManager.getOffsetDateTime(rs, 2));
|
||||||
assertNotNull(QueryManager.getTimestamp(rs, "datetime_col"));
|
assertNotNull(QueryManager.getTimestamp(rs, "datetime_col"));
|
||||||
assertNotNull(QueryManager.getTimestamp(rs, 2));
|
assertNotNull(QueryManager.getTimestamp(rs, 2));
|
||||||
assertEquals("A", QueryManager.getObject(rs, "char_col"));
|
assertEquals("A", QueryManager.getObject(rs, "char_col"));
|
||||||
assertEquals("A", QueryManager.getObject(rs, 3));
|
assertEquals("A", QueryManager.getObject(rs, 3));
|
||||||
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
|
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
|
||||||
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4));
|
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -249,41 +239,39 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testGetValueMethodsReturningNull() throws SQLException
|
void testGetValueMethodsReturningNull() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col) VALUES (null, null, null)");
|
||||||
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col) VALUES (null, null, null)");
|
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from test_table");
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from test_table");
|
preparedStatement.execute();
|
||||||
preparedStatement.execute();
|
ResultSet rs = preparedStatement.getResultSet();
|
||||||
ResultSet rs = preparedStatement.getResultSet();
|
rs.next();
|
||||||
rs.next();
|
|
||||||
|
|
||||||
assertNull(QueryManager.getInteger(rs, "int_col"));
|
assertNull(QueryManager.getInteger(rs, "int_col"));
|
||||||
assertNull(QueryManager.getInteger(rs, 1));
|
assertNull(QueryManager.getInteger(rs, 1));
|
||||||
assertNull(QueryManager.getLong(rs, "int_col"));
|
assertNull(QueryManager.getLong(rs, "int_col"));
|
||||||
assertNull(QueryManager.getLong(rs, 1));
|
assertNull(QueryManager.getLong(rs, 1));
|
||||||
assertNull(QueryManager.getByteArray(rs, "int_col"));
|
assertNull(QueryManager.getByteArray(rs, "int_col"));
|
||||||
assertNull(QueryManager.getByteArray(rs, 1));
|
assertNull(QueryManager.getByteArray(rs, 1));
|
||||||
assertNull(QueryManager.getObject(rs, "int_col"));
|
assertNull(QueryManager.getObject(rs, "int_col"));
|
||||||
assertNull(QueryManager.getObject(rs, 1));
|
assertNull(QueryManager.getObject(rs, 1));
|
||||||
assertNull(QueryManager.getBigDecimal(rs, "int_col"));
|
assertNull(QueryManager.getBigDecimal(rs, "int_col"));
|
||||||
assertNull(QueryManager.getBigDecimal(rs, 1));
|
assertNull(QueryManager.getBigDecimal(rs, 1));
|
||||||
assertNull(QueryManager.getBoolean(rs, "int_col"));
|
assertNull(QueryManager.getBoolean(rs, "int_col"));
|
||||||
assertNull(QueryManager.getBoolean(rs, 1));
|
assertNull(QueryManager.getBoolean(rs, 1));
|
||||||
assertNull(QueryManager.getDate(rs, "datetime_col"));
|
assertNull(QueryManager.getDate(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getDate(rs, 2));
|
assertNull(QueryManager.getDate(rs, 2));
|
||||||
assertNull(QueryManager.getCalendar(rs, "datetime_col"));
|
assertNull(QueryManager.getCalendar(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getCalendar(rs, 2));
|
assertNull(QueryManager.getCalendar(rs, 2));
|
||||||
assertNull(QueryManager.getLocalDate(rs, "datetime_col"));
|
assertNull(QueryManager.getLocalDate(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getLocalDate(rs, 2));
|
assertNull(QueryManager.getLocalDate(rs, 2));
|
||||||
assertNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
|
assertNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getLocalDateTime(rs, 2));
|
assertNull(QueryManager.getLocalDateTime(rs, 2));
|
||||||
assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
|
assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getOffsetDateTime(rs, 2));
|
assertNull(QueryManager.getOffsetDateTime(rs, 2));
|
||||||
assertNull(QueryManager.getTimestamp(rs, "datetime_col"));
|
assertNull(QueryManager.getTimestamp(rs, "datetime_col"));
|
||||||
assertNull(QueryManager.getTimestamp(rs, 2));
|
assertNull(QueryManager.getTimestamp(rs, 2));
|
||||||
assertNull(QueryManager.getObject(rs, "char_col"));
|
assertNull(QueryManager.getObject(rs, "char_col"));
|
||||||
assertNull(QueryManager.getObject(rs, 3));
|
assertNull(QueryManager.getObject(rs, 3));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -295,39 +283,37 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testLocalDate() throws SQLException
|
void testLocalDate() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, "INSERT INTO test_table (date_col) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1));
|
||||||
QueryManager.executeUpdate(connection, "INSERT INTO test_table (date_col) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1));
|
|
||||||
|
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT date_col from test_table");
|
PreparedStatement preparedStatement = connection.prepareStatement("SELECT date_col from test_table");
|
||||||
preparedStatement.execute();
|
preparedStatement.execute();
|
||||||
ResultSet rs = preparedStatement.getResultSet();
|
ResultSet rs = preparedStatement.getResultSet();
|
||||||
rs.next();
|
rs.next();
|
||||||
|
|
||||||
Date date = QueryManager.getDate(rs, 1);
|
Date date = QueryManager.getDate(rs, 1);
|
||||||
assertEquals(1, date.getDate(), "Date value");
|
assertEquals(1, date.getDate(), "Date value");
|
||||||
assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value");
|
assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value");
|
||||||
assertEquals(2013, date.getYear() + 1900, "Year value");
|
assertEquals(2013, date.getYear() + 1900, "Year value");
|
||||||
|
|
||||||
LocalDate localDate = QueryManager.getLocalDate(rs, 1);
|
LocalDate localDate = QueryManager.getLocalDate(rs, 1);
|
||||||
assertEquals(1, localDate.getDayOfMonth(), "Date value");
|
assertEquals(1, localDate.getDayOfMonth(), "Date value");
|
||||||
assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value");
|
assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value");
|
||||||
assertEquals(2013, localDate.getYear(), "Year value");
|
assertEquals(2013, localDate.getYear(), "Year value");
|
||||||
|
|
||||||
LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1);
|
LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1);
|
||||||
assertEquals(1, localDateTime.getDayOfMonth(), "Date value");
|
assertEquals(1, localDateTime.getDayOfMonth(), "Date value");
|
||||||
assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value");
|
assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value");
|
||||||
assertEquals(2013, localDateTime.getYear(), "Year value");
|
assertEquals(2013, localDateTime.getYear(), "Year value");
|
||||||
assertEquals(0, localDateTime.getHour(), "Hour value");
|
assertEquals(0, localDateTime.getHour(), "Hour value");
|
||||||
assertEquals(0, localDateTime.getMinute(), "Minute value");
|
assertEquals(0, localDateTime.getMinute(), "Minute value");
|
||||||
|
|
||||||
OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1);
|
OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1);
|
||||||
assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value");
|
assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value");
|
||||||
assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value");
|
assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value");
|
||||||
assertEquals(2013, offsetDateTime.getYear(), "Year value");
|
assertEquals(2013, offsetDateTime.getYear(), "Year value");
|
||||||
assertEquals(0, offsetDateTime.getHour(), "Hour value");
|
assertEquals(0, offsetDateTime.getHour(), "Hour value");
|
||||||
assertEquals(0, offsetDateTime.getMinute(), "Minute value");
|
assertEquals(0, offsetDateTime.getMinute(), "Minute value");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -338,48 +324,47 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testLocalTime() throws SQLException
|
void testLocalTime() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
|
||||||
////////////////////////////////////
|
|
||||||
// insert one just hour & minutes //
|
|
||||||
////////////////////////////////////
|
|
||||||
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 1, LocalTime.of(10, 42));
|
|
||||||
|
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1");
|
////////////////////////////////////
|
||||||
preparedStatement.execute();
|
// insert one just hour & minutes //
|
||||||
ResultSet rs = preparedStatement.getResultSet();
|
////////////////////////////////////
|
||||||
rs.next();
|
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 1, LocalTime.of(10, 42));
|
||||||
|
|
||||||
LocalTime localTime = QueryManager.getLocalTime(rs, 1);
|
PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1");
|
||||||
assertEquals(10, localTime.getHour(), "Hour value");
|
preparedStatement.execute();
|
||||||
assertEquals(42, localTime.getMinute(), "Minute value");
|
ResultSet rs = preparedStatement.getResultSet();
|
||||||
assertEquals(0, localTime.getSecond(), "Second value");
|
rs.next();
|
||||||
|
|
||||||
localTime = QueryManager.getLocalTime(rs, "time_col");
|
LocalTime localTime = QueryManager.getLocalTime(rs, 1);
|
||||||
assertEquals(10, localTime.getHour(), "Hour value");
|
assertEquals(10, localTime.getHour(), "Hour value");
|
||||||
assertEquals(42, localTime.getMinute(), "Minute value");
|
assertEquals(42, localTime.getMinute(), "Minute value");
|
||||||
assertEquals(0, localTime.getSecond(), "Second value");
|
assertEquals(0, localTime.getSecond(), "Second value");
|
||||||
|
|
||||||
/////////////////////////////////
|
localTime = QueryManager.getLocalTime(rs, "time_col");
|
||||||
// now insert one with seconds //
|
assertEquals(10, localTime.getHour(), "Hour value");
|
||||||
/////////////////////////////////
|
assertEquals(42, localTime.getMinute(), "Minute value");
|
||||||
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 2, LocalTime.of(10, 42, 59));
|
assertEquals(0, localTime.getSecond(), "Second value");
|
||||||
|
|
||||||
preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=2");
|
/////////////////////////////////
|
||||||
preparedStatement.execute();
|
// now insert one with seconds //
|
||||||
rs = preparedStatement.getResultSet();
|
/////////////////////////////////
|
||||||
rs.next();
|
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 2, LocalTime.of(10, 42, 59));
|
||||||
|
|
||||||
localTime = QueryManager.getLocalTime(rs, 1);
|
preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=2");
|
||||||
assertEquals(10, localTime.getHour(), "Hour value");
|
preparedStatement.execute();
|
||||||
assertEquals(42, localTime.getMinute(), "Minute value");
|
rs = preparedStatement.getResultSet();
|
||||||
assertEquals(59, localTime.getSecond(), "Second value");
|
rs.next();
|
||||||
|
|
||||||
localTime = QueryManager.getLocalTime(rs, "time_col");
|
localTime = QueryManager.getLocalTime(rs, 1);
|
||||||
assertEquals(10, localTime.getHour(), "Hour value");
|
assertEquals(10, localTime.getHour(), "Hour value");
|
||||||
assertEquals(42, localTime.getMinute(), "Minute value");
|
assertEquals(42, localTime.getMinute(), "Minute value");
|
||||||
assertEquals(59, localTime.getSecond(), "Second value");
|
assertEquals(59, localTime.getSecond(), "Second value");
|
||||||
}
|
|
||||||
|
localTime = QueryManager.getLocalTime(rs, "time_col");
|
||||||
|
assertEquals(10, localTime.getHour(), "Hour value");
|
||||||
|
assertEquals(42, localTime.getMinute(), "Minute value");
|
||||||
|
assertEquals(59, localTime.getSecond(), "Second value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -390,29 +375,27 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testExecuteStatementForSingleValue() throws SQLException
|
void testExecuteStatementForSingleValue() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, """
|
||||||
QueryManager.executeUpdate(connection, """
|
INSERT INTO test_table
|
||||||
INSERT INTO test_table
|
( int_col, datetime_col, char_col, date_col, time_col )
|
||||||
( int_col, datetime_col, char_col, date_col, time_col )
|
VALUES
|
||||||
VALUES
|
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
||||||
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
""");
|
||||||
""");
|
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1"));
|
||||||
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1"));
|
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table"));
|
||||||
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table"));
|
assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table"));
|
||||||
assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table"));
|
assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table"));
|
||||||
assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table"));
|
assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table"));
|
||||||
assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table"));
|
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 FROM test_table"));
|
||||||
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 FROM test_table"));
|
|
||||||
|
|
||||||
QueryManager.executeUpdate(connection, """
|
QueryManager.executeUpdate(connection, """
|
||||||
INSERT INTO test_table
|
INSERT INTO test_table
|
||||||
( int_col, datetime_col, char_col, date_col, time_col )
|
( int_col, datetime_col, char_col, date_col, time_col )
|
||||||
VALUES
|
VALUES
|
||||||
( null, null, null, null, null)
|
( null, null, null, null, null)
|
||||||
""");
|
""");
|
||||||
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS NULL"));
|
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS NULL"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -423,19 +406,17 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testQueryForSimpleEntity() throws SQLException
|
void testQueryForSimpleEntity() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, """
|
||||||
QueryManager.executeUpdate(connection, """
|
INSERT INTO test_table
|
||||||
INSERT INTO test_table
|
( int_col, datetime_col, char_col, date_col, time_col )
|
||||||
( int_col, datetime_col, char_col, date_col, time_col )
|
VALUES
|
||||||
VALUES
|
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
||||||
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
""");
|
||||||
""");
|
SimpleEntity simpleEntity = QueryManager.executeStatementForSimpleEntity(connection, "SELECT * FROM test_table");
|
||||||
SimpleEntity simpleEntity = QueryManager.executeStatementForSimpleEntity(connection, "SELECT * FROM test_table");
|
assertNotNull(simpleEntity);
|
||||||
assertNotNull(simpleEntity);
|
assertEquals(47, simpleEntity.get("INT_COL"));
|
||||||
assertEquals(47, simpleEntity.get("INT_COL"));
|
assertEquals("Q", simpleEntity.get("CHAR_COL"));
|
||||||
assertEquals("Q", simpleEntity.get("CHAR_COL"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -446,19 +427,17 @@ class QueryManagerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testQueryForRows() throws SQLException
|
void testQueryForRows() throws SQLException
|
||||||
{
|
{
|
||||||
try(Connection connection = getConnection())
|
Connection connection = getConnection();
|
||||||
{
|
QueryManager.executeUpdate(connection, """
|
||||||
QueryManager.executeUpdate(connection, """
|
INSERT INTO test_table
|
||||||
INSERT INTO test_table
|
( int_col, datetime_col, char_col, date_col, time_col )
|
||||||
( int_col, datetime_col, char_col, date_col, time_col )
|
VALUES
|
||||||
VALUES
|
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
||||||
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
|
""");
|
||||||
""");
|
List<Map<String, Object>> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table");
|
||||||
List<Map<String, Object>> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table");
|
assertNotNull(rows);
|
||||||
assertNotNull(rows);
|
assertEquals(47, rows.get(0).get("INT_COL"));
|
||||||
assertEquals(47, rows.get(0).get("INT_COL"));
|
assertEquals("Q", rows.get(0).get("CHAR_COL"));
|
||||||
assertEquals("Q", rows.get(0).get("CHAR_COL"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -82,7 +82,6 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportOutput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
@ -1196,7 +1195,6 @@ public class QJavalinImplementation
|
|||||||
countInput.setTimeoutSeconds(DEFAULT_COUNT_TIMEOUT_SECONDS);
|
countInput.setTimeoutSeconds(DEFAULT_COUNT_TIMEOUT_SECONDS);
|
||||||
countInput.setQueryJoins(processQueryJoinsParam(context));
|
countInput.setQueryJoins(processQueryJoinsParam(context));
|
||||||
countInput.setIncludeDistinctCount(QJavalinUtils.queryParamIsTrue(context, "includeDistinct"));
|
countInput.setIncludeDistinctCount(QJavalinUtils.queryParamIsTrue(context, "includeDistinct"));
|
||||||
countInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
|
|
||||||
CountAction countAction = new CountAction();
|
CountAction countAction = new CountAction();
|
||||||
CountOutput countOutput = countAction.execute(countInput);
|
CountOutput countOutput = countAction.execute(countInput);
|
||||||
@ -1252,7 +1250,6 @@ public class QJavalinImplementation
|
|||||||
queryInput.setShouldGenerateDisplayValues(true);
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
queryInput.setShouldTranslatePossibleValues(true);
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
queryInput.setTimeoutSeconds(DEFAULT_QUERY_TIMEOUT_SECONDS);
|
queryInput.setTimeoutSeconds(DEFAULT_QUERY_TIMEOUT_SECONDS);
|
||||||
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
|
||||||
|
|
||||||
PermissionsHelper.checkTablePermissionThrowing(queryInput, TablePermissionSubType.READ);
|
PermissionsHelper.checkTablePermissionThrowing(queryInput, TablePermissionSubType.READ);
|
||||||
|
|
||||||
|
@ -113,19 +113,16 @@ public class TestUtils
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void primeTestDatabase() throws Exception
|
public static void primeTestDatabase() throws Exception
|
||||||
{
|
{
|
||||||
ConnectionManager connectionManager = new ConnectionManager();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
|
Connection connection = connectionManager.getConnection(TestUtils.defineDefaultH2Backend());
|
||||||
try(Connection connection = connectionManager.getConnection(TestUtils.defineDefaultH2Backend()))
|
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
||||||
|
assertNotNull(primeTestDatabaseSqlStream);
|
||||||
|
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||||
|
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||||
|
String joinedSQL = String.join("\n", lines);
|
||||||
|
for(String sql : joinedSQL.split(";"))
|
||||||
{
|
{
|
||||||
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
QueryManager.executeUpdate(connection, sql);
|
||||||
assertNotNull(primeTestDatabaseSqlStream);
|
|
||||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
|
||||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
|
||||||
String joinedSQL = String.join("\n", lines);
|
|
||||||
for(String sql : joinedSQL.split(";"))
|
|
||||||
{
|
|
||||||
QueryManager.executeUpdate(connection, sql);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,10 +135,8 @@ public class TestUtils
|
|||||||
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
||||||
{
|
{
|
||||||
ConnectionManager connectionManager = new ConnectionManager();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
try(Connection connection = connectionManager.getConnection(defineDefaultH2Backend()))
|
Connection connection = connectionManager.getConnection(defineDefaultH2Backend());
|
||||||
{
|
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
||||||
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,18 +60,16 @@ public class TestUtils
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void primeTestDatabase() throws Exception
|
public static void primeTestDatabase() throws Exception
|
||||||
{
|
{
|
||||||
ConnectionManager connectionManager = new ConnectionManager();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend()))
|
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||||
|
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
||||||
|
assertNotNull(primeTestDatabaseSqlStream);
|
||||||
|
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||||
|
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||||
|
String joinedSQL = String.join("\n", lines);
|
||||||
|
for(String sql : joinedSQL.split(";"))
|
||||||
{
|
{
|
||||||
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
QueryManager.executeUpdate(connection, sql);
|
||||||
assertNotNull(primeTestDatabaseSqlStream);
|
|
||||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
|
||||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
|
||||||
String joinedSQL = String.join("\n", lines);
|
|
||||||
for(String sql : joinedSQL.split(";"))
|
|
||||||
{
|
|
||||||
QueryManager.executeUpdate(connection, sql);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +82,8 @@ public class TestUtils
|
|||||||
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
||||||
{
|
{
|
||||||
ConnectionManager connectionManager = new ConnectionManager();
|
ConnectionManager connectionManager = new ConnectionManager();
|
||||||
try(Connection connection = connectionManager.getConnection(defineBackend()))
|
Connection connection = connectionManager.getConnection(defineBackend());
|
||||||
{
|
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
||||||
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user