Stored queries literals with type only - #4409
Conversation
…type # Conflicts: # docs/sphinx/source/reference/sql_commands/DDL/CREATE/STORED_QUERY.rst
| @Nonnull | ||
| private final Optional<EvaluationContext> evaluationContext; | ||
|
|
||
| @VisibleForTesting |
There was a problem hiding this comment.
I had to remove Optional from the parameters to pass Teamscale
| * because the cache is write-only during warm-up — the lookup would always miss anyway, and the freshly planned | ||
| * entry is written regardless. See {@link #equals}. | ||
| */ | ||
| private final boolean offlinePlanning; |
There was a problem hiding this comment.
Im not really pleased of this flag, but I did not find other option to skip cache lookup at offline planning step.
There was a problem hiding this comment.
This looks like a design leak; we're spilling the concept of offline planning to this internal level. Ideally, we should not touch the plan cache, and gracefully handle the comparison between literalled-COV and unliteralled-COV under certain assumptions.
There was a problem hiding this comment.
agree! I remove offlinePlanning flag from the design and added MissingBindingException which QueryPlanConstraint catches at compileTimeEval and treats as false
not sure that it is the best approach, please review
| * @return a value-free {@link ConstantObjectValue} of the declared type | ||
| */ | ||
| @Nonnull | ||
| public Value processTypedPreparedParam(@Nonnull final String typeName, final int tokenIndex) { |
There was a problem hiding this comment.
I named it PreparedParam because ?{type} grammar belongs to block
| * to {@code NULL} (e.g. JDBC {@code setNull}), whose runtime type is likewise {@code NULL}. | ||
| */ | ||
| @Nonnull | ||
| private static Type primitiveTypeForName(@Nonnull final String typeName) { |
There was a problem hiding this comment.
really do not like such manual parsing but did not find different way
| * When {@code true}, planning runs offline (stored-query warm-up): the plan cache is write-only, so the cache | ||
| * lookup is forced to miss rather than matching the freshly planned entry against the value-free warm-up context. | ||
| */ | ||
| private final boolean offlinePlanning; |
There was a problem hiding this comment.
again don't like such flags, but it is needed to skip cache lookup. open to redesign
| // constant id stays unbound. The declared type is applied during planning (see ExpressionVisitor). | ||
| if (allowTokenAddition) { | ||
| sqlCanonicalizer.append("?").append(" "); | ||
| parameterHash.putInt(Objects.hash("?")); |
There was a problem hiding this comment.
I believe the parameter hash is only used to validate physical plan continuation, so this should be fine, considering that this special type of parameter is only relevant for warming up the plan cache and nothing else, in other words, it shall never be used in a plan execution context.
…nding exception and do not match QueryPlanConstraint
Adds inline typed parameters
?{type}to stored-query bodies, e.g.:CREATE STORED QUERY by_col1 AS SELECT * FROM t1 WHERE col1 > ?{bigint}A
?{type}declares only a type, not a value, so the stored query is planned value-free at warm-up: one cached plan is reused by any runtime query that binds a value of the same type at the same position (e.g.... WHERE col1 > ?). This avoids authoring a stored query per concrete literal.Grammar:
?{type}is a single lexer token (TYPED_PARAMETER). Being one token preserves the parameter'sconstantIdand canonicalizes to?, so a typed body shares the plan-cache key with a runtime?.Planning: the parameter becomes an unbound
ConstantObjectValueof the declared type - no value seeded, no binding in the warmupEvaluationContext. The plan constraint isOfType(declared)+IS NOT NULL(orIS NULLfor?{null}), i.e. value-independent.Null: the special
?{null}declares the nullableNULLtype and warms a plan constrained byOF TYPE NULL+IS NULL, matching a runtime parameter bound toNULL(e.g. JDBCsetNull). A typed non-null parameter and?{null}warm separate cache entries.Filtered indexes: a value-free parameter cannot drive filtered/range-index selection (the planner needs a concrete value). Such a stored query is skipped at warmup and logged; it still plans normally at runtime when a value is present. Use a concrete literal for those.
Offline warm-up: warm-up planning writes plans to the cache but never reads from it, so the cache lookup is skipped. There is nothing to match with anyway — a value-free stored query has no bound values, and matching a cached entry needs concrete values to evaluate its constraint. Skipping is safe (the lookup would always miss during warm-up) and each freshly planned entry is still written.
Supported types: primitives (
BOOLEAN,INTEGER,BIGINT,FLOAT,DOUBLE,STRING,BYTES),UUID, and the specialNULL.The runtime value's raw type must match the declared type exactly (e.g.
?{bigint}is reused by aLONGvalue —setLong, or a15Lliteral - not a bareINTEGER).Non-null by default: an ordinary
?{type}carries anIS NOT NULLconstraint and is not a nullable placeholder; use?{null}to warm theNULLcase.Boolean cases are still authored as separate stored queries with concrete literals.