Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ private static boolean isAsCall(SqlNode node) {
}

CalciteCatalogReader catalogReader = this.catalogReader.withSchemaPath(schemaPath);
SqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters());
IgniteSqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters());
validator.allowTechnicalColumns(ctx.allowTechnicalColumns());
SqlToRelConverter sqlToRelConverter = sqlToRelConverter(validator, catalogReader, sqlToRelConverterCfg);
RelRoot root = sqlToRelConverter.convertQuery(sqlNode, true, false);
root = root.withRel(sqlToRelConverter.decorrelate(sqlNode, root.rel));
Expand Down Expand Up @@ -425,8 +426,13 @@ public String dump() {

/** */
private SqlValidator validator() {
if (validator == null)
validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters());
if (validator == null) {
IgniteSqlValidator v = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters());

v.allowTechnicalColumns(ctx.allowTechnicalColumns());

validator = v;
}

return validator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
/** */
private final RelDataType nullType;

/** Whether technical column access is allowed (for internal queries). */
private boolean allowTechnicalColumns;

/**
* Creates a validator.
*
Expand All @@ -135,6 +138,17 @@ public IgniteSqlValidator(
nullType = typeFactory.createSqlType(SqlTypeName.NULL);
}

/**
* Allows or prohibits technical column access during validation.
* Internal Ignite queries may set this to {@code true} to plan queries
* that reference technical columns such as {@code _ver} and {@code _src}.
*
* @param allow {@code true} to allow technical column access.
*/
public void allowTechnicalColumns(boolean allow) {
allowTechnicalColumns = allow;
}

/** {@inheritDoc} */
@Override public void validateInsert(SqlInsert insert) {
validateTableModify(insert.getTargetTable());
Expand Down Expand Up @@ -246,6 +260,29 @@ private void validateTableModify(SqlNode table) {
super.validateSelect(select, targetRowType);
}

/** {@inheritDoc} */
@Override protected void validateOrderList(SqlSelect select) {
super.validateOrderList(select);

SqlNodeList orderList = select.getOrderList();

if (orderList == null)
return;

for (SqlNode orderItem : orderList) {
SqlNode node = orderItem;

// Unwrap DESC / NULLS FIRST / NULLS LAST wrappers to get the actual expression.
while (node.getKind() == SqlKind.DESCENDING
|| node.getKind() == SqlKind.NULLS_FIRST
|| node.getKind() == SqlKind.NULLS_LAST) {
node = ((SqlCall)node).operand(0);
}

validateTechnicalColumnAccess(node);
}
}

/** {@inheritDoc} */
@Override protected void validateNamespace(SqlValidatorNamespace namespace, RelDataType targetRowType) {
SqlValidatorTable table = namespace.getTable();
Expand Down Expand Up @@ -293,7 +330,7 @@ else if (n instanceof SqlDynamicParam) {
if (call.getKind() == SqlKind.AS) {
final String alias = deriveAlias(call, 0);

if (isSystemFieldName(alias))
if (QueryUtils.isReservedFieldName(alias))
throw newValidationError(call, IgniteResource.INSTANCE.illegalAlias(alias));
}
else if (call.getKind() == SqlKind.CAST) {
Expand Down Expand Up @@ -411,18 +448,26 @@ private SqlNode rewriteTableToQuery(SqlNode from) {
SelectScope scope,
boolean includeSysVars
) {
if (!includeSysVars && exp.getKind() == SqlKind.IDENTIFIER && isSystemFieldName(deriveAlias(exp, 0))) {
SqlQualified qualified = scope.fullyQualify((SqlIdentifier)exp);
if (!includeSysVars && exp.getKind() == SqlKind.IDENTIFIER) {
String alias = deriveAlias(exp, 0);

if (qualified.namespace == null)
// Technical columns are never exposed via SELECT *.
if (QueryUtils.isTechnicalFieldNameIgnoreCase(alias))
return;

if (qualified.namespace.getTable() != null) {
// If child is table and has only system fields, expand star to these fields.
// Otherwise, expand star to non-system fields only.
for (RelDataTypeField fld : qualified.namespace.getRowType().getFieldList()) {
if (!isSystemField(fld))
return;
if (QueryUtils.isReservedFieldName(alias)) {
SqlQualified qualified = scope.fullyQualify((SqlIdentifier)exp);

if (qualified.namespace == null)
return;

if (qualified.namespace.getTable() != null) {
// If child is table and has only system fields, expand star to these fields.
// Otherwise, expand star to non-system fields only.
for (RelDataTypeField fld : qualified.namespace.getRowType().getFieldList()) {
if (!isSystemField(fld))
return;
}
}
}
}
Expand All @@ -432,7 +477,7 @@ private SqlNode rewriteTableToQuery(SqlNode from) {

/** {@inheritDoc} */
@Override public boolean isSystemField(RelDataTypeField field) {
return isSystemFieldName(field.getName());
return QueryUtils.isReservedFieldName(field.getName());
}

/** */
Expand Down Expand Up @@ -551,14 +596,10 @@ private IgniteTypeFactory typeFactory() {
return (IgniteTypeFactory)typeFactory;
}

/** */
private boolean isSystemFieldName(String alias) {
return QueryUtils.KEY_FIELD_NAME.equalsIgnoreCase(alias)
|| QueryUtils.VAL_FIELD_NAME.equalsIgnoreCase(alias);
}

/** {@inheritDoc} */
@Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) {
validateTechnicalColumnAccess(expr);

if (expr instanceof SqlDynamicParam) {
RelDataType type = deriveDynamicParameterType((SqlDynamicParam)expr, nullType);

Expand All @@ -569,6 +610,24 @@ private boolean isSystemFieldName(String alias) {
return super.deriveType(scope, expr);
}

/** */
private void validateTechnicalColumnAccess(SqlNode expr) {
if (allowTechnicalColumns)
return;

if (!(expr instanceof SqlIdentifier))
return;

SqlIdentifier id = (SqlIdentifier)expr;

String fieldName = id.names.get(id.names.size() - 1);

if (id.isStar() || !QueryUtils.isTechnicalFieldNameIgnoreCase(fieldName))
return;

throw newValidationError(id, IgniteResource.INSTANCE.cannotAccessTechnicalColumn(id.toString()));
}

/** @return A derived type or {@code null} if unable to determine. */
@Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node, RelDataType nullValType) {
RelDataType type = getValidatedNodeTypeIfKnown(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,26 @@ public final class PlanningContext implements Context {
/** */
private final long plannerTimeout;

/** Whether technical column access is allowed (for internal queries). */
private final boolean allowTechnicalColumns;

/**
* Private constructor, used by a builder.
*/
private PlanningContext(
Context parentCtx,
String qry,
@Nullable Object[] parameters,
long plannerTimeout
long plannerTimeout,
boolean allowTechnicalColumns
) {
this.qry = qry;
this.parameters = parameters;

this.parentCtx = parentCtx;
startTs = U.currentTimeMillis();
this.plannerTimeout = plannerTimeout;
this.allowTechnicalColumns = allowTechnicalColumns;
}

/**
Expand Down Expand Up @@ -115,6 +120,13 @@ public long plannerTimeout() {
return plannerTimeout;
}

/**
* @return {@code true} if technical column access is allowed during query planning.
*/
public boolean allowTechnicalColumns() {
return allowTechnicalColumns;
}

/**
* @return Schema.
*/
Expand Down Expand Up @@ -221,6 +233,9 @@ public static class Builder {
/** */
private long plannerTimeout;

/** */
private boolean allowTechnicalColumns;

/**
* @param parentCtx Parent context.
* @return Builder for chaining.
Expand Down Expand Up @@ -259,13 +274,25 @@ public Builder plannerTimeout(long plannerTimeout) {
return this;
}

/**
* Allows technical column access ({@code _ver}, {@code _src}) during query planning.
* Must only be used for internal Ignite queries, never for user-facing SQL.
*
* @param allow {@code true} to allow technical column access.
* @return Builder for chaining.
*/
public Builder allowTechnicalColumns(boolean allow) {
this.allowTechnicalColumns = allow;
return this;
}

/**
* Builds planner context.
*
* @return Planner context.
*/
public PlanningContext build() {
return new PlanningContext(parentCtx, qry, parameters, plannerTimeout);
return new PlanningContext(parentCtx, qry, parameters, plannerTimeout, allowTechnicalColumns);
}
}
}
Loading
Loading