-
Notifications
You must be signed in to change notification settings - Fork 1
aql_subquery_implementation
Stand: 5. Dezember 2025
Version: 1.0.0
Kategorie: Aql
Feature: Full Subquery and Common Table Expression (CTE) Support
Branch: feature/aql-st-functions
Completion Date: 17. November 2025
Total Effort: ~28 Stunden (Phase 3: 14h + Phase 4: 14h)
ThemisDB unterstützt jetzt vollständig:
- WITH-Klausel für Common Table Expressions (CTEs)
- Scalar Subqueries in LET und RETURN Expressions
- Correlated Subqueries mit Zugriff auf äußere Variablen
- ANY/ALL Quantifiers mit Subquery-Support
- Automatic Memory Management mit Spill-to-Disk für große CTEs
- Performance Optimization mit Materialization Heuristics
AST Nodes:
-
WithNode- WITH-Klausel Container -
CTEDefinition- einzelne CTE Definition (name + subquery) -
SubqueryExpr- Subquery in Expression -
AnyExpr/AllExpr- Quantified predicates
Parser Extensions:
-
parseWithClause()- parstWITH name AS (subquery), ... -
parsePrimaryExpression()- erkennt(FOR ... RETURN ...)als Subquery -
parseQuantifiedExpression()- parstANY x IN arr SATISFIES pred
Files:
-
include/query/aql_ast.h- AST node definitions -
src/query/aql_parser.cpp- parsing logic
CTE Processing:
-
AQLTranslator::translate()sammelt CTEs aus WITH-Klausel -
countCTEReferences()zählt CTE-Verwendungen rekursiv -
SubqueryOptimizer::shouldMaterializeCTE()entscheidet Materialisierung -
attachCTEs()fügt CTE metadata zu TranslationResult hinzu
Data Structures:
-
TranslationResult::CTEExecution- CTE metadata (name, subquery, should_materialize) -
vector<CTEExecution> ctes- attached to all success results
Files:
-
include/query/aql_translator.h- CTEExecution struct, declarations -
src/query/aql_translator.cpp- CTE collection and optimization logic
CTE Execution:
-
QueryEngine::executeCTEs()- führt CTE-Liste sequentiell aus - Für jede CTE: translate → execute (based on type) → store in context
- Unterstützt alle Query-Typen: Join, Conjunctive, Disjunctive, VectorGeo, ContentGeo
Subquery Execution:
-
evaluateExpression()SubqueryExpr case - recursive translation & execution - Creates child context via
ctx.createChild()for correlation - Executes CTEs if present, then main subquery
- Returns scalar (single), null (empty), or array (multiple) results
CTE References in FOR:
-
executeJoin()checksctx.getCTE(collection)before table scan - Nested-loop join iterates CTE results instead of table
- Hash-join builds/probes from CTE results
Files:
-
include/query/query_engine.h- executeCTEs declaration, parent_context param -
src/query/query_engine.cpp- executeCTEs, SubqueryExpr, CTE iteration logic
CTECache Design:
- In-memory cache with configurable limit (default 100MB)
- Automatic spill-to-disk when threshold exceeded
- Sample-based size estimation (first 10 elements → extrapolate)
- LRU-style eviction (largest-first)
- Binary spill format: count + (size + json_data) pairs
- Transparent loading on access
- Auto-cleanup on destruction
Integration:
-
EvaluationContext::cte_cache- shared_ptr across contexts -
storeCTE()/getCTE()- cache-first with fallback to in-memory map -
createChild()- shares cache pointer with child contexts -
executeJoin()- initializes cache with default config
Statistics:
-
total_ctes,in_memory_ctes,spilled_ctes -
memory_usage_bytes,total_results -
spill_operations,disk_reads
Files:
-
include/query/cte_cache.h- CTECache class (156 lines) -
src/query/cte_cache.cpp- Implementation (338 lines)
Basic CTE:
WITH expensive_hotels AS (
FOR h IN hotels
FILTER h.price > 200
RETURN h
)
FOR doc IN expensive_hotels
RETURN doc.name
Multiple CTEs:
WITH
expensive AS (FOR h IN hotels FILTER h.price > 200 RETURN h),
berlin AS (FOR e IN expensive FILTER e.city == "Berlin" RETURN e)
FOR doc IN berlin
RETURN doc
CTE Dependencies: CTEs können vorherige CTEs referenzieren (sequential execution).
In LET:
FOR user IN users
LET avgAge = (FOR u IN users RETURN AVG(u.age))
RETURN {user: user.name, avgAge: avgAge[0]}
In RETURN:
FOR user IN users
RETURN {
name: user.name,
orderCount: LENGTH((FOR o IN orders FILTER o.userId == user._key RETURN o))
}
LET with Correlation:
FOR user IN users
LET userOrders = (FOR o IN orders FILTER o.userId == user._key RETURN o)
RETURN {user: user.name, orders: userOrders}
FILTER with Correlation:
FOR user IN users
FILTER (FOR o IN orders FILTER o.userId == user._key RETURN o) != []
RETURN user
ANY:
FOR doc IN users
FILTER ANY tag IN doc.tags SATISFIES tag == "premium"
RETURN doc
ALL:
FOR order IN orders
FILTER ALL item IN order.items SATISFIES item.price < 100
RETURN order
With Subqueries:
FOR user IN users
FILTER ANY order IN (FOR o IN orders FILTER o.userId == user._key RETURN o)
SATISFIES order.total > 1000
RETURN user
Nested in LET:
FOR doc IN orders
LET enriched = (
FOR product IN products
FILTER product.id == (FOR item IN doc.items RETURN item.productId LIMIT 1)[0]
RETURN product
)
RETURN {order: doc, product: enriched}
Subqueries with CTEs:
FOR doc IN orders
LET enriched = (
WITH expensive AS (FOR p IN products FILTER p.price > 100 RETURN p)
FOR ep IN expensive FILTER ep.id == doc.productId RETURN ep
)
RETURN {order: doc, product: enriched}
Default Config:
CTECache::Config config;
config.max_memory_bytes = 100 * 1024 * 1024; // 100MB
config.spill_directory = "./themis_cte_spill";
config.enable_compression = false; // Future optimization
config.auto_cleanup = true;Custom Config (Future): Via QueryEngine constructor or configuration file.
When:
-
store()estimates CTE size - If
current_usage + new_cte_size > max_memory_bytes:- Call
makeRoom(new_cte_size) - Find largest in-memory CTE
- Spill to disk if >= required bytes
- Call
Size Estimation:
- Sample first 10 elements
- Serialize to JSON
- Calculate average size
- Extrapolate:
avg_size * total_count + overhead
Binary Format:
[count: uint64_t]
[size1: uint64_t][data1: json bytes]
[size2: uint64_t][data2: json bytes]
...
On Destruction:
- Remove all spill files
- Remove spill directory if empty
- Reset statistics
Manual Cleanup:
-
cache.clear()- removes all CTEs and spill files -
cache.remove(name)- removes specific CTE
SubqueryOptimizer::shouldMaterializeCTE():
-
Always Materialize:
- Multiple references (ref_count > 1)
- Used in aggregate functions
- Used in GROUP BY or SORT
-
Consider Inlining:
- Single reference (ref_count == 1)
- Simple filter-only queries
- Small estimated result size
Hash-Join with CTEs:
- Build phase checks
getCTE()for build table - Probe phase checks
getCTE()for probe table - CTE results bypass table scan
Predicate Pushdown:
- Single-variable filters pushed down to CTE iteration
- Multi-variable filters applied after join
Phase 3 Tests:
-
ScalarSubqueryInLet- Subquery in LET expression -
NestedSubquery- Multi-level subquery nesting -
AnyQuantifier- ANY with array iteration -
AllQuantifier- ALL with array iteration -
WithClauseSingleCTE- Single CTE parsing -
WithClauseMultipleCTEs- Multiple CTE parsing -
CTEWithFilters- Complex CTE queries
Phase 4 Tests:
-
SubqueryExecution_ScalarResult- Single value return -
SubqueryExecution_ArrayResult- Multiple value return -
SubqueryExecution_NestedSubqueries- Subquery in LET + FILTER -
SubqueryExecution_WithCTE- Subquery containing WITH clause -
SubqueryExecution_CorrelatedSubquery- Outer variable reference -
SubqueryExecution_InReturnExpression- Subquery in RETURN object
Basic Operations:
-
BasicStoreAndGet- Store and retrieve CTE -
MultipleCTEs- Multiple CTEs in cache -
RemoveCTE- Remove specific CTE
Spill-to-Disk:
-
AutomaticSpillToDisk- Trigger spill with large data -
MultipleSpills- Multiple CTEs exceed memory -
SpillFileCleanup- Auto-cleanup on destruction
Memory Management:
-
MemoryUsageTracking- Track memory consumption -
ClearCache- Clear all CTEs -
StatsAccumulation- Statistics collection
Edge Cases:
-
EmptyResults- Empty CTE -
NonExistentCTE- Access non-existent CTE -
OverwriteCTE- Overwrite existing CTE
-
No Compression:
- Spill files use uncompressed JSON
- Future: Add zstd compression option
-
No Query Plan Caching:
- CTEs are re-translated on every query
- Future: Cache translation results
-
No Parallel CTE Execution:
- CTEs executed sequentially
- Future: Detect independent CTEs, execute in parallel
-
Simple Eviction Strategy:
- Largest-first eviction
- Future: LRU or access-frequency based
-
No Distributed Execution:
- CTEs execute on single node
- Future: Distribute large CTEs across cluster
A. Window Functions (10-14h):
- ROW_NUMBER(), RANK(), DENSE_RANK()
- LEAD(), LAG()
- PARTITION BY, ORDER BY
- Frame specifications (ROWS/RANGE)
B. Advanced JOINs (16-20h):
- LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN
- ON clause syntax
- JOIN optimization (reordering, statistics)
C. Query Plan Caching (6-8h):
- Cache TranslationResult by query hash
- Invalidate on schema change
- LRU eviction
D. CTE Enhancements (4-6h):
- RECURSIVE CTEs (tree traversal)
- Compression in spill files
- Parallel CTE execution
- Persistent CTE materialization
E. Subquery Optimizations (8-10h):
- Subquery to JOIN rewrite
- IN (subquery) optimization
- EXISTS optimization
- Semi-join / Anti-join
New Files:
-
include/query/cte_cache.h- 156 lines -
src/query/cte_cache.cpp- 338 lines -
tests/test_cte_cache.cpp- 330 lines -
docs/SUBQUERY_IMPLEMENTATION_SUMMARY.md- this file
Modified Files:
-
include/query/aql_ast.h- +80 lines (AST nodes) -
src/query/aql_parser.cpp- +250 lines (parsing logic) -
include/query/aql_translator.h- +35 lines (CTEExecution, declarations) -
src/query/aql_translator.cpp- +180 lines (CTE collection, reference counting) -
include/query/query_engine.h- +25 lines (executeCTEs, cache integration) -
src/query/query_engine.cpp- +400 lines (executeCTEs, SubqueryExpr, CTE iteration) -
tests/test_aql_subqueries.cpp- +150 lines (execution tests) -
CMakeLists.txt- +2 lines (cte_cache.cpp, test_cte_cache.cpp)
Total: ~1800 lines of new/modified code
No Breaking Changes:
- All existing queries continue to work
- CTEs are opt-in via WITH clause
- Subqueries are opt-in via parenthesized FOR
When to Use CTEs:
- Multiple references to same subquery
- Complex filtering that should be materialized
- Readability improvement for complex queries
When to Avoid:
- Single-use subqueries (inlining may be faster)
- Very large result sets (consider streaming)
- Simple filters (better to inline)
Default (100MB): Suitable for most workloads.
Large Datasets:
Consider increasing max_memory_bytes if:
- Frequent spill operations (check stats)
- Fast SSD available for spill directory
- Memory is abundant
Small Environments:
Consider decreasing max_memory_bytes if:
- Limited RAM
- Many concurrent queries
- Small CTEs typical
Documentation:
-
docs/PHASE_3_PLAN.md- Parsing & AST design -
docs/PHASE_4_PLAN.md- Execution & memory management -
docs/AQL_GRAMMAR.md- Updated grammar with subqueries
Code:
-
include/query/aql_ast.h- AST definitions -
include/query/aql_translator.h- Translation interface -
include/query/query_engine.h- Execution interface -
include/query/cte_cache.h- Memory management
Tests:
-
tests/test_aql_subqueries.cpp- Parser & execution tests -
tests/test_cte_cache.cpp- Memory management tests
- Implementation: AI Assistant (GitHub Copilot)
- Design Review: mkrueger
- Testing: Automated test suite
Last Updated: 17. November 2025
Version: 1.0
Status: Production Ready (pending compilation verification)
- Architecture-ACCESS-MODEL-IMPLEMENTATION-SUMMARY
- Architecture-ADR-003-pg-dump-sql-parser
- Architecture-BASEENTITY-PRINCIPLE
- Architecture-CACHE-STORAGE-INTEGRATION
- Architecture-CMAKE-ARCHITECTURE
- Architecture-CMAKE-FLAGS-REFERENCE
- Architecture-CMAKE-MODULAR-ARCHITECTURE
- Architecture-CONCERNS-ARCHITECTURE-DIAGRAM
- Architecture-CONCERNS-IMPLEMENTATION-SUMMARY
- Architecture-CONTENT-MODEL
- Architecture-COPILOT-THEMISDB-GRAPH-RAG-BACKEND-ARCHITECTURE
- Architecture-CRYPTO-AND-KEYS
- Architecture-FEATURE-FLAGS-REFERENCE
- Architecture-GPU-ARCHITECTURE-REVIEW-TEMPLATE
- Architecture-HTTP-SHUTDOWN-HARDENING
- Architecture-MIGRATION-GUIDE-CONCERNS
- Architecture-MIGRATION-GUIDE-v13-v14
- Architecture-MODULARIZATION-GUIDE
- Architecture-MODULAR-ARCHITECTURE-ROADMAP
- Architecture-MODULE-ARCHITECTURE-INDEX
- Architecture-P1D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D08-MAMBA-GOVERNANCE-CONTRACT
- Architecture-P1-P2-IMPLEMENTATION-COMPLETION-INDEX
- Architecture-PHASE0-COMPLETION-ASSESSMENT
- Architecture-PHASE3-QUERYENGINE-DI-ARCHITECTURE
- Architecture-PHASE4-INDEX-MANAGER-DI
- Architecture-POSTGRESQL-WIRE-PROTOCOL
- Architecture-QUERYENGINE-IMPLEMENTATION-GUIDE
- Architecture-QUERY-SCHEDULING
- Architecture-RAFT-CONSENSUS-DESIGN
- Architecture-README
- Architecture-README-SSM-HYBRID-IMPLEMENTATION
- Architecture-REFACTORING-SUMMARY
- Architecture-RESOURCE-POOLING
- Architecture-SOURCE-DIRECTORY-GUIDE
- Architecture-THEMIS-CORE-GUIDE
- Architecture-UNIFIED-ACCESS-MODEL
- Architecture-WAL-GRPC-MTLS-CONFIGURATION
- Architecture-WIRE-PROTOCOL-RETRY
- Architecture-boltzmann-observability-draft
- Architecture-experimental-logarithmic-vector-storage
- Architecture-llm-wiki-mvp-adr
- Architecture-rewrite-engine-architecture
- Architecture-rope-api-architecture
- Architecture-ssm-gguf-mamba-status
- Architecture-ssm-hybrid-analysis
- Architecture-ssm-hybrid-rollout-plan
- Architecture-ssm-plugin-interface-design-review
- Architecture-transaction-coordinators
- Architecture-wiki-secondary-index
- Architecture-wire-protocol
- Governance-DISABLED-STUB-POLICY
- Governance-DOCS-PR-POLICY
- Governance-GA-PROMOTION-SIGN-OFF
- Governance-GITHUB-MILESTONES-SETUP
- Governance-MATURITY-CLAIM-VERIFICATION-CHECKLIST
- Governance-MATURITY-EVIDENCE-REGISTRY
- Governance-MERGE-GATE-BOT-CONFIG
- Governance-MERGE-GATE-STATUS-LIVE
- Governance-PHASE3-ENFORCEMENT-RUNBOOK
- Governance-PHASE-1-CLOSURE-REPORT
- Governance-PHASE-CLOSURE-POLICY
- Governance-PHASE-DEPENDENCY-GRAPH
- Governance-PLUGIN-SUBMODULE-ROLLBACK
- Governance-PRODUCTION-READY-2026-DELIVERY-PLAN
- Governance-PR-VERSION-TARGETING
- Governance-PR-VERSION-TARGETING-BACKFILL
- Governance-QUERY-MODULE-STATUS
- Governance-README
- Governance-RELEASE-PROMOTION-GATE-POLICY
- Governance-RELEASE-VALIDATION-CHECKLIST
- Governance-SECURITY-MODULE-5671-EVIDENCE-SUMMARY
- Governance-SHARDING-P6-RESIDUAL-RISK-ACCEPTANCE
- Governance-SOURCECODE-COMPLIANCE-GOVERNANCE
- Governance-UPDATES-DEVELOPMENT-STATUS-SIGN-OFF
- Governance-WAVE-C-IMPLEMENTATION-COMPLETE
- Module-acceleration-Roadmap
- Module-access-model-Roadmap
- Module-ai-Roadmap
- Module-analytics-Roadmap
- Module-api-Roadmap
- Module-aql-Roadmap
- Module-auth-Roadmap
- Module-base-Roadmap
- Module-cache-Roadmap
- Module-cdc-Roadmap
- Module-chaos-Roadmap
- Module-chimera-Roadmap
- Module-config-Roadmap
- Module-content-Roadmap
- Module-core-Roadmap
- Module-distributed-knowledge-Roadmap
- Module-distributed-tensor-Roadmap
- Module-document-Roadmap
- Module-ethics-ai-Roadmap
- Module-evaluation-Roadmap
- Module-execution-Roadmap
- Module-exporters-Roadmap
- Module-failover-Roadmap
- Module-geo-Roadmap
- Module-governance-Roadmap
- Module-gpu-Roadmap
- Module-graph-Roadmap
- Module-image-analysis-Roadmap
- Module-importers-Roadmap
- Module-index-Roadmap
- Module-ingestion-Roadmap
- Module-llama-cpp-Roadmap
- Module-llm-Roadmap
- Module-llm-streaming-Roadmap
- Module-llm-wiki-Roadmap
- Module-maintenance-Roadmap
- Module-metadata-Roadmap
- Module-network-Roadmap
- Module-observability-Roadmap
- Module-onnx-clip-Roadmap
- Module-performance-Roadmap
- Module-plugins-Roadmap
- Module-process-Roadmap
- Module-projects-Roadmap
- Module-prompt-engineering-Roadmap
- Module-query-Roadmap
- Module-rag-Roadmap
- Module-replication-Roadmap
- Module-retrieval-Roadmap
- Module-rpc-grpc-Roadmap
- Module-scheduler-Roadmap
- Module-scraper-Roadmap
- Module-search-Roadmap
- Module-security-Roadmap
- Module-server-Roadmap
- Module-sharding-Roadmap
- Module-stable-diffusion-Roadmap
- Module-storage-Roadmap
- Module-temporal-Roadmap
- Module-tensor-Roadmap
- Module-themis-Roadmap
- Module-timeseries-Roadmap
- Module-toolbox-Roadmap
- Module-training-Roadmap
- Module-transaction-Roadmap
- Module-updates-Roadmap
- Module-user-storage-encrypted-Roadmap
- Module-utils-Roadmap
- Module-vector-search-Roadmap
- Module-voice-Roadmap
- Module-whisper-Roadmap