Skip to content

Side effects in scope management #7

@nickBes

Description

@nickBes

There are a couple of places introducing side effect updates of scope which introduces possibility of unknown behavior.

Here are some examples:

visitQuery = (ctx: QueryContext): void => {
const withCtx = ctx.with();
if (withCtx) {
const cteScope: CteScope = { tables: new Map() };
// `using` guarantees the CTE scope is popped when this if-block exits,
// even if an exception is thrown during CTE registration or query traversal.
using _cteScope = this.pushCteScope(cteScope);
this.registerCtes(withCtx, cteScope);
this.visit(ctx.queryNoWith());
} else {
this.visit(ctx.queryNoWith());
}
};

private registerPatternRecognition(ctx: PatternRecognitionContext, scope: QueryScope): void {
if (!ctx.MATCH_RECOGNIZE()) {
this.registerAliasedRelation(ctx.aliasedRelation(), scope);
return;
}
const aliasCtx = ctx.identifier();
const alias = aliasCtx ? getIdentifierText(aliasCtx) : null;
const columnAliasCtx = ctx.columnAliases();
const measureCols = columnAliasCtx
? columnAliasCtx.identifier().map(getIdentifierText)
: ctx.measureDefinition().map((m) => getIdentifierText(m.identifier()));
if (alias) {
scope.tables.set(normalizeId(alias), createScopeTable(alias, measureCols, "derived"));
}
}

Preferred behavior would look like

  visitQuery = (ctx: QueryContext): void => {
    const withCtx = ctx.with();
    if (withCtx) {
      // calculate scope
      const cteScope = this.getScopeFromCte(withCtx);
      using _cteScope = this.pushCteScope(cteScope);
      this.visit(withCtx); // then visit
      this.visit(ctx.queryNoWith());
    } else {
      this.visit(ctx.queryNoWith());
    }
  };

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions