Skip to content

driver-memory's analytics generateSql() renders the LIKE family with NO wildcards, so the echoed statement is an EQUALITY the pipeline never ran #7117

Description

@os-zhuang

Found while implementing #6520 ($icontains on every JS evaluation face). Out of that card's scope — it is the $contains family, not $icontains, and it predates that work — so it is filed rather than fixed there.

Measured on origin/main @ f5a9bc2f3, by reading the source

MemoryAnalyticsService has two exits for one normalized filter tree, and they disagree about what the LIKE family MEANS.

The mingo exit (query()) builds a real containment pattern — memory-analytics.ts, CUBE_OPERATOR_TO_MONGO_PREDICATE:

contains: ({ raw, substring }) => ({ $regex: substring(raw[0]) }),

where substring is the driver's own filterSubstringPattern(value) = new RegExp(escapeRegex(value), 'i') — a substring match.

The SQL exit (generateSql()) emits the comparand as a bare literal, with no % anywhere (memory-analytics.ts, the WHERE builder):

const sqlOp = this.operatorToSql(filter.operator);          // 'contains' -> 'LIKE'
const comparand = this.comparandsFor(cube, filter.member, filter.values)[0];
whereClauses.push(`${fieldPath} ${sqlOp} ${this.toSqlLiteral(comparand)}`);

toSqlLiteral only quotes and escapes quotes; it adds no wildcards. So { name: { $contains: 'acme' } } echoes

WHERE name LIKE 'acme'

which is an equality (case-folded on SQLite, exact on Postgres), while query() returns every row containing acme.

notContains has the mirror of the same bug via NOT LIKE.

Why this matters

This is the #5333 / #3650 class — "a rendering that contradicts execution is worse than no rendering" — reached through driver-memory's analytics face instead of service-analytics' echo. An author who runs the echoed statement to reproduce a chart gets a narrower row set than the chart, so the filter reads as broken in the opposite direction from #5333's widening. service-analytics has an enumerated regression test for exactly this property on its own three compilers (objectql-echo-operator-coverage.test.ts); driver-memory's analytics generateSql() has none, which is why this survived.

Note the direction is wrong-rendering, not a permission bypass: this exit produces display SQL, not the executed query.

Scope

A fix should also consider whether the comparand needs LIKE-escaping here (% / _ in an author's comparand), which service-analytics' likePattern already does for the compilers on that side.

Suggested check

Pin generateSql()'s WHERE against query()'s row set for each member of the LIKE family, the way objectql-echo-operator-coverage.test.ts pins the echo against execution — an assertion on the SQL string alone would not have caught the missing % either.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions