Class JoinsContext
Overview: When a backend action (e.g., RDBMSQueryAction,
RDBMSCountAction, RDBMSAggregateAction) needs to generate
SQL (or an equivalent query for other backends), it constructs a
JoinsContext from the main table name, the caller-supplied list of
QueryJoin objects, and the QQueryFilter. During construction,
this class modifies the join list and the filter to include everything
needed for a correct, secure query.
Initialization pipeline (init()):
- Process existing QueryJoins: validates that each join's
table exists, and populates the
aliasToTableNameMapso that later steps can resolve aliases to real table names. ensureFilterIsRepresented: scans all filter criteria, other-field-names, and order-bys fortable.fieldreferences. If a referenced table is not yet in the join list, a join is added for it (with metadata resolved from the instance).ensureRecordSecurityLockIsRepresented(main table): for each read-scopeRecordSecurityLockon the main table, ensures that any joins needed to reach the lock's security field are present, and builds security filter criteria.fillInMissingJoinMetaData: for joins that were added without explicitQJoinMetaData(e.g., only a table name was specified), finds matching metadata from theQInstance's joins. Also resolves indirect/multi-hop joins viaExposedJoinpaths on the main table.ensureAllJoinRecordSecurityLocksAreRepresented: iterates over all joined tables and applies their security locks as well, potentially adding further implicit joins.addSecurityFiltersToInputFilter: merges the accumulated security filter into the original input filter. If the input filter usesOR, it is wrapped in a newANDalongside the security filter.
Alias management: The internal aliasToTableNameMap maps
both aliases and bare table names to actual table names. It is populated
as joins are processed. Use resolveTableNameOrAliasToTableName(java.lang.String)
to convert any alias/name to the real table name.
Security lock pipeline: When a RecordSecurityLock has a
non-empty joinNameChain, the chain is walked (in reverse) to add
ImplicitQueryJoinForSecurityLock joins as needed. The resulting
security criteria are placed either in the JOIN's ON clause (via
QueryJoin.withSecurityCriteria(java.util.List<com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria>)) or in the WHERE clause,
depending on whether the lock is part of an OR filter or applies
to the main table directly.
Key consumers: AbstractRDBMSAction (uses this context to
build FROM, WHERE, ORDER BY, and GROUP BY
clauses), MemoryRecordStore, and GenerateReportAction.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordRecord pairing a resolvedQFieldMetaDatawith the table name or alias from which it was resolved. -
Constructor Summary
ConstructorsConstructorDescriptionJoinsContext(QInstance instance, String tableName, List<QueryJoin> queryJoins, QQueryFilter filter) Primary constructor.JoinsContext(String tableName, QQueryFilter filter, boolean omitSecurity) Constructor that starts with an empty join list and optionally skips security-lock processing.JoinsContext(String tableName, List<QueryJoin> queryJoins, QQueryFilter filter) -
Method Summary
Modifier and TypeMethodDescriptionfindJoinMetaData(String baseTableName, String joinTableName, boolean useExposedJoins) Searches theQInstance's joins for aQJoinMetaDatathat connectsbaseTableNametojoinTableName(checking both directions and flipping if needed).getFieldAndTableNameOrAlias(String fieldName) Parses a field reference (optionally prefixed withtableOrAlias.) and returns the resolvedQFieldMetaDatatogether with the table name or alias it belongs to.Returns the live list ofQueryJoinobjects in this context.booleanhasAliasOrTable(String tableOrAlias) Check if the given tableOrAlias exists in the query - but note, if a table is in the query, but with an alias, then it would not be found by this method.booleanCheck if the given table name exists in the query - but that name may NOT be an alias - it must be an actual table name.resolveTableNameOrAliasToTableName(String nameOrAlias) Resolves a name (which may be an alias or an actual table name) to the real table name that can be passed toqInstance.getTable().
-
Constructor Details
-
JoinsContext
public JoinsContext(String tableName, List<QueryJoin> queryJoins, QQueryFilter filter) throws QException - Parameters:
tableName- the main (root) table for the query.queryJoins- caller-supplied list of joins; may be empty. Additional joins may be added during initialization.filter- the query filter; may be mutated to include security criteria.- Throws:
QException- if a referenced table or join cannot be resolved.
-
JoinsContext
public JoinsContext(QInstance instance, String tableName, List<QueryJoin> queryJoins, QQueryFilter filter) throws QException Primary constructor.- Parameters:
instance- the QInstance containing table and join metadata.tableName- the main (root) table for the query.queryJoins- caller-supplied list of joins; may be empty. Additional joins may be added during initialization.filter- the query filter; may be mutated to include security criteria.- Throws:
QException- if a referenced table or join cannot be resolved.
-
JoinsContext
Constructor that starts with an empty join list and optionally skips security-lock processing. Obtains theQInstancefromQContext.- Parameters:
tableName- the main (root) table for the query.filter- the query filter; may be mutated to include security criteria (unlessomitSecurityistrue).omitSecurity- iftrue, the initialization pipeline will not process anyRecordSecurityLocks or add security filters.- Throws:
QException- if a referenced table or join cannot be resolved.
-
-
Method Details
-
getQueryJoins
-
resolveTableNameOrAliasToTableName
Resolves a name (which may be an alias or an actual table name) to the real table name that can be passed toqInstance.getTable(). If the name is not found in the alias map, it is returned as-is.- Parameters:
nameOrAlias- an alias or table name present in this context.- Returns:
- the actual table name.
-
getFieldAndTableNameOrAlias
Parses a field reference (optionally prefixed withtableOrAlias.) and returns the resolvedQFieldMetaDatatogether with the table name or alias it belongs to.If
fieldNamecontains a dot (e.g.,"department.name"), the part before the dot is treated as a table name or alias and resolved via the alias map. If there is no dot, the field is looked up on the main table.- Parameters:
fieldName- a field name, optionally qualified astable.field.- Returns:
- a
JoinsContext.FieldAndTableNameOrAliasrecord. - Throws:
IllegalArgumentException- if the name has more than one dot, or the table/field cannot be found.
-
hasTable
Check if the given table name exists in the query - but that name may NOT be an alias - it must be an actual table name. e.g., Given: FROM `order` INNER JOIN line_item li hasTable("order") => true hasTable("li") => false hasTable("line_item") => true -
hasAliasOrTable
Check if the given tableOrAlias exists in the query - but note, if a table is in the query, but with an alias, then it would not be found by this method. e.g., Given: FROM `order` INNER JOIN line_item li hasAliasOrTable("order") => false hasAliasOrTable("li") => true hasAliasOrTable("line_item") => false -
findJoinMetaData
public QJoinMetaData findJoinMetaData(String baseTableName, String joinTableName, boolean useExposedJoins) Searches theQInstance's joins for aQJoinMetaDatathat connectsbaseTableNametojoinTableName(checking both directions and flipping if needed).If multiple matches are found and
useExposedJoinsistrue, the main table'sExposedJoinlist is consulted to disambiguate. If disambiguation fails, aRuntimeExceptionis thrown.- Parameters:
baseTableName- the "left" / base table (may benullto match any table already in this context).joinTableName- the "right" / target table.useExposedJoins- whether to use exposed joins for disambiguation when multiple matches are found.- Returns:
- the matching
QJoinMetaData, ornullif none found. - Throws:
RuntimeException- if more than one join matches and disambiguation fails.
-