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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions