Class AbstractSqlExpressionConverter<C extends SqlConversionContext>

java.lang.Object
de.calamanari.adl.cnv.AbstractExpressionConverter<de.calamanari.adl.irl.CoreExpression, QueryTemplateWithParameters, C>
de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C>
de.calamanari.adl.sql.cnv.AbstractSqlExpressionConverter<C>
Type Parameters:
C - SqlConversionContext or derived type
All Implemented Interfaces:
de.calamanari.adl.cnv.ExpressionConverter<de.calamanari.adl.irl.CoreExpression, QueryTemplateWithParameters>, de.calamanari.adl.irl.CoreExpressionVisitor
Direct Known Subclasses:
DefaultSqlExpressionConverter

public abstract class AbstractSqlExpressionConverter<C extends SqlConversionContext> extends de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C>
Base class for creating SQL-converters.

A couple of common functions have been identified and realized as template methods to be overridden as desired.
In general methods can be found here that are related to preparation steps before composing the final SQL-statement from the prepared data.

The state of a conversion is quite complex. Because I wanted to make instances reusable (so state must be reset to initial settings), I moved all the variables into the ResettableScpContext. Now, each SqlConversionContext issued by the converter for a particular level of the expression being converted has the full picture in form of the injected process state of the converter. This allows global state communication (variables, flags) throughout a conversion process.

Author:
Karl Eilebrecht
  • Constructor Details

    • AbstractSqlExpressionConverter

      protected AbstractSqlExpressionConverter(Supplier<? extends C> contextSupplier, ResettableScpContext processContext)
      Creates a new instance of the converter, fully prepared to call AbstractExpressionConverter.convert(Object).

      Meant for sub-classes that want to provide an individual processContext (or a sub-class).

      Parameters:
      contextSupplier - to create a context for each level of the expression we visit
      processContext -
      See Also:
    • AbstractSqlExpressionConverter

      protected AbstractSqlExpressionConverter(Supplier<? extends C> contextSupplier, DataBinding dataBinding, Map<String, Serializable> globalVariables, Set<de.calamanari.adl.Flag> flags)
      Creates a new instance of the converter, fully prepared to call AbstractExpressionConverter.convert(Object).

      Important: The set of global variables and flags you specify here act as a template for each subsequent run. A conversion run will never modify the map with the variables and the set with the flags provided at construction time because it will run on independent copies.

      Parameters:
      contextSupplier - to create a context for each level of the expression we visit
      dataBinding -
      globalVariables - optional global variables (null means empty)
      flags - directives and dynamic flags (null means empty)
      See Also:
    • AbstractSqlExpressionConverter

      protected AbstractSqlExpressionConverter(Supplier<? extends C> contextSupplier, DataBinding dataBinding)
      Creates a new instance of the converter, fully prepared to call AbstractExpressionConverter.convert(Object).
      Parameters:
      contextSupplier - to create a context for each level of the expression we visit
      dataBinding -
  • Method Details

    • getAugmentationListener

      public SqlAugmentationListener getAugmentationListener()
      Returns:
      augmentation listener of this instance or SqlAugmentationListener.none() if not installed, not null
    • setAugmentationListener

      public void setAugmentationListener(SqlAugmentationListener augmentationListener)
      Parameters:
      augmentationListener - null means SqlAugmentationListener.none() (default)
    • getProcessContext

      public final SqlConversionProcessContext getProcessContext()
      Returns the converter's process context across the levels of a conversion.

      While each level of an expression's DAG gets its own level context AbstractExpressionConverter.getContext(), AbstractExpressionConverter.getParentContext(), AbstractExpressionConverter.getRootContext()), the process context (global variables and general converter state) is common to all of them and allows data exchange.

      By sharing the process context callers have access to the full data and feature set of the converter without sharing the converter instance itself.

      Returns:
      the current global process context of the converter (variables, flags, etc.), instance of ResettableScpContext
    • init

      public void init()
      Overrides:
      init in class de.calamanari.adl.cnv.AbstractExpressionConverter<de.calamanari.adl.irl.CoreExpression, QueryTemplateWithParameters, C extends SqlConversionContext>
    • getIdColumnName

      public final String getIdColumnName()
      Returns:
      id-column name for joining and the returned result, defaults to SqlFormatConstants.DEFAULT_ID_COLUMN_NAME
    • setIdColumnName

      public final void setIdColumnName(String idColumnName)
      Parameters:
      idColumnName - id-column name for joining and the returned result, defaults to SqlFormatConstants.DEFAULT_ID_COLUMN_NAME
    • getQueryType

      public final QueryType getQueryType()
      Returns:
      type of the query to be created, defaults to QueryType.SELECT_DISTINCT_ID_ORDERED
    • setQueryType

      public final void setQueryType(QueryType queryType)
      Parameters:
      queryType - type of the query to be created, defaults to QueryType.SELECT_DISTINCT_ID_ORDERED
    • getInitialVariables

      public final Map<String, Serializable> getInitialVariables()
      Returns the variable configuration passed to the constructor

      This configuration acts as a template for all subsequent calls to AbstractExpressionConverter.convert(Object).

      You can use this method if you did not specify any variables at construction time but instead want to define them now (before calling AbstractExpressionConverter.convert(Object)).

      Returns:
      mutable map
    • getInitialFlags

      public final Set<de.calamanari.adl.Flag> getInitialFlags()
      Returns the flag configuration passed to the constructor

      This configuration acts as a template for all subsequent calls to AbstractExpressionConverter.convert(Object)

      You can use this method if you did not specify any flags at construction time but instead want to define them now (before calling AbstractExpressionConverter.convert(Object)).

      Returns:
      mutable set
    • getMainTable

      public final TableMetaInfo getMainTable()
      The main table is the table to start the query with. It is initialized with DataTableConfig.primaryTable().

      Depending on the conversion strategy it can be reasonable to override it with a different table, for example if only a single table is involved without any complex joins.

      Returns:
      the main table or null if not configured
    • setMainTable

      protected final void setMainTable(TableMetaInfo mainTable)
      The main table is the table to start the query with. It is initialized with DataTableConfig.primaryTable().

      Depending on the conversion strategy it can be reasonable to override it with a different table, for example if only a single table is involved without any complex joins.

      Parameters:
      mainTable - the table to prefer to start the query or null to enforce another query start strategy
    • getStyle

      public final de.calamanari.adl.FormatStyle getStyle()
      Returns:
      configured formatting style (inline or multi-line)
    • setStyle

      public final void setStyle(de.calamanari.adl.FormatStyle style)
      Parameters:
      style - formatting style (inline or multi-line)
    • stats

      protected final CoreExpressionStats stats()
      Returns:
      expression statistics from the expression helper
    • aliasHelper

      protected final AliasHelper aliasHelper()
      Returns:
      alias helper for this conversion, initialized during prepareRootExpression()
    • expressionHelper

      protected final CoreExpressionSqlHelper expressionHelper()
      Returns:
      expression helper for the currently processed expression, initialized during prepareRootExpression()
    • dataBinding

      protected final DataBinding dataBinding()
      Returns:
      initially configured data binding (physical data model)
    • whereClause

      protected final StringBuilder whereClause()
      Returns:
      WHERE-clause builder
    • conditionFactory

      protected final MatchConditionFactory conditionFactory()
      Returns:
      match condition factory of the current run
    • style

      protected final de.calamanari.adl.FormatStyle style()
      Returns:
      configured format style (pretty or inline)
    • flags

      protected final Set<de.calamanari.adl.Flag> flags()
      Returns:
      process flags (mutable)
    • globalVariables

      protected final Map<String, Serializable> globalVariables()
      Returns:
      global process variables (mutable)
    • tablesInWhereClause

      protected final Set<TableMetaInfo> tablesInWhereClause()
      Returns:
      collection of the tables referenced in the where clause
    • aliasesInWhereClause

      protected final Set<ExpressionAlias> aliasesInWhereClause()
      Returns:
      collection of the aliases referenced in the where clause
    • augmentationListener

      protected final SqlAugmentationListener augmentationListener()
      Returns:
      the augmentation listener of this converter or SqlAugmentationListener.none() if not installed, never null
    • getMainIdColumnName

      protected final String getMainIdColumnName()
      Resolves the main id-column for joining further tables.

      The effective name of the ID-column can vary if the tables have custom-IDs and there are aliases or a renamed base selection.
      This method simply tests if there is a main table (then the result is the column name) or not (then the ID is getIdColumnName().

      Note: This is the raw name, of course finally the returned ID-column name is always getIdColumnName() (renamed if required).

      Returns:
      effective main id for joining
    • prepareRootExpression

      protected de.calamanari.adl.irl.CoreExpression prepareRootExpression()
      Overrides:
      prepareRootExpression in class de.calamanari.adl.cnv.AbstractExpressionConverter<de.calamanari.adl.irl.CoreExpression, QueryTemplateWithParameters, C extends SqlConversionContext>
    • createAliasHelper

      protected AliasHelper createAliasHelper()
      This method allows sub-classes to replace the helper with a custom one
      Returns:
      new helper instance
    • createCoreExpressionSqlHelper

      protected CoreExpressionSqlHelper createCoreExpressionSqlHelper(de.calamanari.adl.irl.CoreExpression rootExpression)
      This method allows sub-classes to replace the helper with a custom one
      Parameters:
      rootExpression - the prepared root expression before start
      Returns:
      new helper instance, a CoreExpressionSqlHelper initialized with the root expression by default
    • createMatchConditionFactory

      protected MatchConditionFactory createMatchConditionFactory()
      This method allows sub-classes to replace the factory with a custom one
      Returns:
      new match condition factory, instance of DefaultMatchConditionFactory by default
    • enterCombinedExpression

      public void enterCombinedExpression(de.calamanari.adl.irl.CombinedExpression expression)
      Overrides:
      enterCombinedExpression in class de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C extends SqlConversionContext>
    • exitCombinedExpression

      public void exitCombinedExpression(de.calamanari.adl.irl.CombinedExpression expression)
      Overrides:
      exitCombinedExpression in class de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C extends SqlConversionContext>
    • handleMatchExpression

      public void handleMatchExpression(de.calamanari.adl.irl.MatchExpression expression)
      Overrides:
      handleMatchExpression in class de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C extends SqlConversionContext>
    • enterNegationExpression

      public void enterNegationExpression(de.calamanari.adl.irl.NegationExpression expression)
      Overrides:
      enterNegationExpression in class de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C extends SqlConversionContext>
    • handleSpecialSetExpression

      public void handleSpecialSetExpression(de.calamanari.adl.irl.SpecialSetExpression expression)
      Overrides:
      handleSpecialSetExpression in class de.calamanari.adl.cnv.AbstractCoreExpressionConverter<QueryTemplateWithParameters, C extends SqlConversionContext>
    • appendAliasUnionBaseQuery

      protected void appendAliasUnionBaseQuery(StringBuilder sb, List<ExpressionAlias> aliases)
      Appends a UNION of the given aliases to compose a super set as base query (part if with)
      Parameters:
      sb -
      aliases -
    • appendAllTableUnionBaseQuery

      protected void appendAllTableUnionBaseQuery(StringBuilder sb)
      Appends a UNION of all tables defined in the data binding.

      This "worst-case" scenario happens if no table is marked as primary table nor containing all IDs and the query contains negative or IS UNKNOWN conditions.

      Parameters:
      sb -
    • appendFullTableQuery

      protected void appendFullTableQuery(StringBuilder sb, TableMetaInfo table)
      Appends a select that queries all IDs of the given table
      Parameters:
      sb -
      table - to select all IDs from (with respect to optional table filter conditions)
    • appendAliasGroupToBaseQuery

      protected void appendAliasGroupToBaseQuery(StringBuilder sb, List<ExpressionAlias> aliasGroup)
      Appends the query related to one alias group (a table or duo of two tables in case of a reference match) to become part of a UNION that shall form the base query.
      Parameters:
      sb -
      aliasGroup -
    • appendAliasQuery

      protected void appendAliasQuery(StringBuilder sb, ExpressionAlias alias)
      Append an alias query (part of the SQL WITH-section)
      Parameters:
      sb -
      alias -
    • appendToAliasConditionClause

      protected void appendToAliasConditionClause(StringBuilder sb, MatchCondition condition, boolean qualified)
      Appends the condition to the given alias WHERE-clause or ON-condition.

      In contrast to the global where clause we don't need extra existence checks in this case (alias is always a simple join).

      Parameters:
      sb -
      condition -
      qualified -
    • appendToGlobalWhereClause

      protected void appendToGlobalWhereClause(StringBuilder sb, MatchCondition condition, ExpressionAlias currentAlias)
      Appends the condition to the global WHERE-clause.

      We always try to work with the regular table names. The alias name only comes into play if there is any multi-row sensitivity or IS-NULL-check.

      Parameters:
      sb -
      condition -
      currentAlias -
      See Also:
    • appendMatchCondition

      protected void appendMatchCondition(StringBuilder sb, MatchCondition condition, boolean qualified)
      Appends the given condition to WHERE clause (with/sub query).

      Note: There is special IS NULL handling, instead of IS NULL we query IS NOT NULL which requires a corresponding handling by the global where clause. Only in the edge case CoreExpressionSqlHelper.isNullQueryingAllowed(String) NULL will be queried directly to simplify the query.

      Parameters:
      sb - to append the condition
      condition - to be appended
      qualified - tells whether the column names must be qualified or not
    • appendAliasExistenceCheckWithExtraNullCheckIfRequired

      protected void appendAliasExistenceCheckWithExtraNullCheckIfRequired(StringBuilder sb, MatchCondition condition, ExpressionAlias alias)
      When we query columns with multi-row sensitivity, we perform the condition check within the alias query and perform an alias existence check in the global WHERE clause (we are not attributing the aliases).

      An extra is-null-check can be required to avoid accidentally including nulls in the result.

      Parameters:
      sb -
      condition -
      alias -
      See Also:
    • appendFilterColumnConditions

      protected void appendFilterColumnConditions(StringBuilder sb, String optionalTableOrAliasName, List<ColumnCondition> columnConditions, boolean followUp)
      Appends the given list of filter column conditions, connected by AND with the previous content
      Parameters:
      sb -
      optionalTableOrAliasName - qualifier for the columns or null (pure column name)
      columnConditions -
      followUp - if true then there is some earlier condition (prepend AND)