Skip to content

SQL Engine & Queries

LoomCache ships a Calcite-backed SQL engine under loom-server/src/main/java/com/loomcache/server/query. Queries target DistributedMap values — think “SELECT from a named map”.

From a client, send a SQL_QUERY_EXECUTE message via the LoomSqlResult helper:

// User must have an explicit supported serializer/encoding before production use.
LoomMap<String, User> users = client.getMap("users");
// Returns a LoomSqlResult wrapping the server's SqlResult
LoomSqlResult result = users.query("SELECT key, name, age FROM users WHERE age > 30");
for (SqlRow row : result.rows()) {
String key = row.getString("key");
int age = row.getInt("age");
...
}

Under the hood:

  • SqlParser — Calcite-backed SELECT parser producing a QueryPlan.
  • QueryAnalyzer — type-checks the query against registered IndexConfigs.
  • QueryOptimizer — builds a QueryPlan, choosing the best MapIndex per predicate.
  • QueryEngine — executes the plan, optionally reusing a previous QueryExecutionCache entry.
  • SlowQueryLog — flags queries exceeding the configured duration.
  • SELECT with projections, aliases, DISTINCT.
  • FROM <mapName> (maps only — queues/sets/lists are not queryable today).
  • WHERE <predicate> with:
    • =, <>, <, <=, >, >=
    • LIKE (SQL patterns)
    • IN, BETWEEN
    • AND, OR, NOT
  • ORDER BY, LIMIT.
  • Aggregates over one map: COUNT, SUM, AVG, MIN, MAX.
  • Joins, GROUP BY, and HAVING are rejected/unsupported in this release.

CREATE INDEX and declarative SQL indexes are unsupported/rejected in this release. Do not use SQL index DDL or declarative <indexes>-style configuration as a Hazelcast-style eager index maintenance or production query acceleration claim. Queries must meet production budgets without relying on indexes.

Attribute extraction is handled by AttributeExtractor — usually a reflective getter, with SingleAttributeProjection / MultiAttributeProjection for projections. Reflective extraction over arbitrary POJOs still depends on the serialization policy: the value type must be explicitly supported on every client and member before production use.

QueryStats + QueryProfiler capture plan cost, rows scanned, index hits, and duration. Slow queries (over a configurable threshold) land in SlowQueryLog and can be tailed from the /api/v1/management/* endpoints.

Queries without an explicit LIMIT are capped at 10,000 result rows to protect heap usage. When that safety cap cuts matching rows, the server logs a WARN and LoomSqlResult.truncated() returns true.

Aggregate queries scan at most 1,000,000 entries. If that safety cap is reached, the server logs a WARN because the aggregate reflects only the scanned prefix.

  • Only DistributedMap values are queryable. Queues / sets / lists have no SQL surface.
  • Joins, GROUP BY, and HAVING are rejected/unsupported in this release; keep bank cutover SQL map-scoped.
  • No transactions — each SQL_QUERY_EXECUTE runs as a single linearizable read.
  • No cross-cluster federation; queries are scoped to the local cluster.
  • The server-side QueryExecutionCache is enabled by default; invalidation is tied to the map-change listener stream.
  • CREATE INDEX and declarative SQL indexes are unsupported/rejected in this release. Treat unbounded full scans as unsupported for bank cutover until query-specific limits and evidence exist.

See Data Structures for what is addressable from the SQL engine today.