8000 Audit which OpenOptions settings should be overridable per query (and which already are) · Issue #862 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

Audit which OpenOptions settings should be overridable per query (and which already are) #862

Open
2 of 4 issues completed

Description

@adityamukho

Goal

Audit every OpenOptions field. Decide which ones should also be overridable per query (or per transaction or per cursor), and record which ones already are. The result should be a table in the docs plus follow-up issues for any gaps worth closing.

Current state (v3, as of 2026-11-05)

OpenOptions field Per-query override today Notes
max_derived_facts Yes: :max-derived-facts N Replaces the database value for that query (#553)
max_results (deprecated alias) Yes: :max-results N The lower of the two applies (#553)
query_memory_limit (QRY-012) Check Confirm whether any per-query, prepared or cursor path can set it
(round limit, not an option yet) No Hard-coded 1000; see #861
transaction_chunk_facts Check Per-transaction rather than per-query; does begin_write take it?
synchronous Check Per-transaction durability (e.g. a bulk import with SyncMode::Normal) may be useful
wal_checkpoint_threshold No Database-wide policy; probably stays open-only
page_cache_size No Shared cache; probably stays open-only
allow_unlocked No Open-time only by nature
read_only No Open-time only by nature

Prepared queries

PreparedQuery copies the database's max_derived_facts and query_memory_limit when it is prepared (src/query/datalog/prepared.rs). Its only per-statement override is a literal :max-derived-facts N in the prepared text. What's missing:

For every option found to be per-query overridable, the audit must also decide the prepared-query form: a literal in the text, a bind slot, a PreparedQuery setter, or a mix. Each form must be exposed in all bindings.

Proposed safety mechanism: OpenOptions::allow_overrides

The embedder controls which settings a query may override. This addresses the safety question raised in question 2 below.

  • The field: OpenOptions::allow_overrides: Vec<Overridable> (allowOverrides: [] in the bindings), where Overridable is a #[non_exhaustive] enum with one variant per overridable setting: MaxDerivedFacts, MaxResults, MaxIterations (Make the recursion round limit (max_iterations) configurable per query and in OpenOptions #861), QueryMemoryLimit, and whatever else this audit adds.
  • Default (decided 2026-11-06): the field holds a default list of variants, used when the embedder doesn't set it.
    • When the embedder sets it, only the listed variants are allowed: the given list replaces the default and is not merged with it. An empty list allows no overrides at all.
    • Adding one setting therefore means restating the defaults. Accepted: every alternative (merge semantics, separate allow/deny lists) is at least as tricky. To soften this, publish the default list as a constant, e.g. Overridable::DEFAULTS, so a caller can write [Overridable::DEFAULTS, &[Overridable::QueryMemoryLimit]].concat(), with an equivalent in each binding.
  • Contents of the default list are decided per option by this audit. Proposal: it holds MaxDerivedFacts and MaxResults, which queries can override today, so existing queries keep working. QueryMemoryLimit stays out of it, so query text can't change the memory cap unless the embedder opts in.
  • Override not allowed: an override of a setting not in the list, whether literal or bind slot, is refused with a new error code (API- or QRY-, registered in docs/ERROR_REFERENCE.md), never silently ignored or clamped.
  • Scope (decided 2026-11-06): the list governs query text only: keywords and their bind slots, including in prepared query text. Settings applied from application code (e.g. a PreparedQuery setter or a binding equivalent) aren't gated, because application code is trusted.
  • Override direction (deferred, 2026-11-06): whether lowering a limit should be allowed for unlisted settings is not part of this work. Revisit only if needed.

Questions to answer per option

  1. Is a per-query, per-transaction or per-cursor override meaningful, or is the setting inherently database-wide (cache, locking, WAL policy)?
  2. If it is meaningful, may the override raise the value above the database's or only lower it? Example: should a single query be able to exceed the query_memory_limit the embedder chose? This is a safety question when query text comes from an untrusted or LLM-generated source.
  3. Where does the override live: query keyword (:max-…), bind slot, PreparedQuery setter, cursor API, begin_write options, or the binding APIs?
  4. How do prepared queries and bind slots interact with it (see v2.x: prepared queries ignore OpenOptions max_derived_facts / max_results #679)?

Deliverables

  • A table of every option in docs/ and on the docs site (performance-tuning.md and/or datalog-reference.md): open-only or overridable, at which level, and raise or lower semantics.
  • One follow-up issue per gap worth closing, each with a philosophy check (zero-configuration: defaults must stay right without any override).
  • Bindings: list where an existing override isn't exposed.

Related: #861 (max_iterations), #553, #549, #679.

Activity

  1. added this to the v3.next milestone on Nov 5, 2026
  2. modified the milestones: v3.next, v3.0.0 on Nov 5, 2026
  3. added a commit that references this issue on Nov 6, 2026
  4. adityamukho commented on Nov 6, 2026

    @adityamukho
    CollaboratorAuthor

    Spec merged in #868. Implementation split into sub-issues: #869 (gate, unblocks #861), #870 (:query-memory-limit, slots, setters), #871 (TxOptions), #872 (bindings). #862 closes when all four are done.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0