Repository navigation
M3: SQL and the point path #4
Description
The fourth milestone of rupg, specified in spec/23-milestones.md section 23.6. M3 runs the SQL that applications run on rows: the full analyzer, INSERT, UPDATE, DELETE, MERGE and RETURNING, MVCC at READ COMMITTED and REPEATABLE READ, prepared statements and the plan cache. It also delivers the point path, the first optimizer, and version 1 of the vectorized executor on the hot store. Gate G4 closes here.
Why
The point path is at M3, not at M8. Gate G4 measures the CPU per operation. A slow protocol layer is hard to fix late, because its cost is spread over every message. The point path sets the cost floor while the code is still small.
M3 also completes the first SQL path that M2 shipped for the catalog queries of G1. The regression suite starts to count here, as a tracked pass count with a ratchet.
The work
Taken from spec/23 §23.6. The reference in brackets names the spec section that owns the detail. Change the list in the spec and copy it again. Do not edit it here.
-
rupg-analyzefor the full grammar, with the error for each statement that rupg does not yet run (spec/07 §7.5) -
INSERT,UPDATE,DELETE,MERGEandRETURNING(spec/23 §23.6) - MVCC with in-place update and undo, at
READ COMMITTEDandREPEATABLE READ(spec/11 §11.2, spec/11 §11.5.1, spec/11 §11.5.2) - The xid exposure (spec/11 §11.9)
- The LSN (spec/11 §11.12)
- Prepared statements and the plan cache (spec/06 §6.9.2, spec/15 §15.10)
- The point path, which runs a prepared single-row statement with no heap allocation (spec/14 §14.9)
- The vectorized executor version 1: scans, filters, projections, hash join, hash aggregate, sort, limit and window functions (spec/14 §14.4, spec/14 §14.7, spec/14 §14.8.1, spec/14 §14.10)
-
rupg-optversion 1 with statistics fromANALYZE(spec/15 §15.3) -
EXPLAINoutput in the format of spec/15 §15.9 (spec/15 §15.9) - The built-in functions that the regression suite calls most, in the order of the count of calls (spec/07 §7.18.3)
- The regression suite, with the pass count of the 239 tests published and ratcheted (spec/21 §21.4.1, spec/21 §21.15)
- sqllogictest (spec/21 §21.4.4)
- The differential generator with TLP, NoREC and PQS each night (spec/21 §21.4.5)
- 48 CPU hours on
sql_parsewith no open find (spec/21 §21.7) - 1 million more crash files with DML and DDL (spec/21 §21.9.4)
- Crates that first ship:
rupg-opt(spec/23 §23.2)
Exit criterion
- G4 (spec/02 §2.10): YCSB workloads A, B, C and F at 3x less server CPU per operation than PostgreSQL, without pipelining. spec/20 §20.13 runs it on the
oltpmachine with 16 clients against PostgreSQL 19, and the ratio must hold on every workload. - The report publishes the pass count of the 239 regression tests, at or above its ratchet.
sql_parseruns 48 CPU hours with no open find.- 1 million more crash files with DML and DDL run with no lost commit.
- The close report answers each line of the checklist in spec/23 §23.18 that applies from M3.
Open questions
These questions in spec/24 close at M3.
- Q12 (spec/24 §24.3):
max_parallel_workers_per_gather, an effective default that differs from the reported value or a gate run that sets it. Q12: What does max_parallel_workers_per_gather report? #25 Closed by Close Q12, Q13, Q21 and Q25 with the M3 evidence #1068: rupg reports 2, and a gate run sets the value and prints it. - Q13 (spec/24 §24.4): float aggregates in a different order, an exclusion for the last bits or PostgreSQL's serial order. Q13: Do float aggregates in a different order match the oracle? #26 Closed by Close Q12, Q13, Q21 and Q25 with the M3 evidence #1068: document 05 item 17 accepts the last bits only for a parallel or segment sum.
- Q18 (spec/24 §24.4): the collation order of ICU4X against ICU4C, decided by a corpus test. Q18: Does ICU4X order strings as ICU4C does? #31
- Q20 (spec/24 §24.4): math functions on other platforms, and correctly rounded functions in Rust. Q20: Do math functions give the same answers on other platforms? #33 Closed by Call cbrt and tgamma of the C library #1067: rupg calls the C math library of the platform, as PostgreSQL does.
- Q21 (spec/24 §24.4): hash values that users can see, with two sets of hashes in
rupg-kernels. Q21: Do hash values that users can see match PostgreSQL? #34 Closed by Close Q12, Q13, Q21 and Q25 with the M3 evidence #1068: the hashes that users see match PostgreSQL 19. - Q25 (spec/24 §24.4):
relpages, bytes divided by 8,192 or a count that keeps external cost formulas as on PostgreSQL. Q25: What does relpages report? #38 Closed by Close Q12, Q13, Q21 and Q25 with the M3 evidence #1068: the page count of PostgreSQL for the same rows. - Q72 (spec/24 §24.9): facts not verified in two cited sources. Q72: Which facts are not verified? #85 Closed by Close Q72 with the release of self-join elimination #1060: PostgreSQL 18 added self-join elimination.
Harness work
- Compatibility: M3: L3 tracked and the generated corpus rupg-compat#3
- Benchmarks: M3: YCSB and the point path rupg-bench#3
Depends on
#3.
Spec
- spec/23-milestones.md §23.6 M3: SQL and the point path
- spec/23-milestones.md §23.16 Why the order is what it is
- spec/02-the-goal.md §2.7.3 YCSB
- spec/02-the-goal.md §2.10 When a gate fails
- spec/07-sql-types-and-catalog.md §7.5 The analyzer
- spec/07-sql-types-and-catalog.md §7.18 Functions and operators, and how they are tested
- spec/11-transactions-and-log.md
- spec/14-execution.md §14.9 The point path
- spec/15-optimizer.md
- spec/20-benchmarks.md §20.9 YCSB
- spec/20-benchmarks.md §20.13 What each gate reads
- spec/21-testing.md §21.4 The suites
- spec/24-open-questions.md §24.3 The 10x targets
- spec/24-open-questions.md §24.4 Compatibility
- spec/24-open-questions.md §24.9 The project
Activity
tamnd commentedon Oct 17, 2026
#391 is merged. It is the first step of the item for
INSERT,UPDATE,DELETE,MERGEandRETURNING, and of the item for MVCC.INSERTwithVALUES, with a query and withDEFAULT VALUESstores them, andSELECTreads them.transformInsertStmtmatch PostgreSQL 19 inconnection/insert.test.READ COMMITTEDtakes a snapshot for each statement, andREPEATABLE READkeeps one for the transaction. A rollback removes the rows.The two items stay open. The next steps are the unique checks,
UPDATE,DELETE,RETURNING, and the move of the rows torupg-txnand the hot store.tamnd commentedon Oct 18, 2026
#392 is merged. It adds
UPDATEandDELETEon the heap tables. Each row version now hasxmin,cmin,xmaxandcmax. A change of a row that a running transaction changed waits with the wait eventLock/transactionid, and it has the deadlock check andlock_timeout. After a commit of the other transaction, read committed runs the statement again and repeatable read gives40001. The output of a single-session script and of a two-session script is the same as the output of PostgreSQL 19, with the exception of the transaction IDs.Known differences: the second run uses a new snapshot for the full statement and not
EvalPlanQual. A table with a unique index, a foreign key, a trigger or a rule,RETURNINGandWITHstill give0A000.Next: the unique and primary key checks on the heap, then the foreign key checks and
RETURNING.tamnd commentedon Oct 18, 2026
#395 is merged.
INSERTandUPDATEnow check the unique indexes and the primary keys. A duplicate key gives23505with the detailKey (...)=(...) already exists., and a key that a transaction in progress holds makes the write wait, with the contextwhile inserting index tuple. The heap keeps a hash map for each unique key, so a run of single-row inserts does not scan the full table. The output of a script of unique keys and of a two-session script is the same as the output of PostgreSQL 19, with the exception of the transaction IDs.Known differences: a unique index on an expression, a partial unique index and a deferrable unique constraint still give
0A000.Release 0.1.42 is out with
INSERT,UPDATEandDELETE(#391 and #392).Next: the foreign key checks, then
RETURNING.tamnd commentedon Oct 18, 2026
#396 adds
RETURNINGtoINSERT,UPDATEandDELETE. The list can use*, expressions, subqueries, and the columns ofFROMandUSING. It also has the namesoldandnewof PostgreSQL 18 and the optionRETURNING WITH (OLD AS o, NEW AS n). The rows go to the client in the simple protocol, the extended protocol andEXECUTE, andDescribegivesRowDescriptionfor a statement withRETURNING. The test script was recorded from PostgreSQL 19 and gives the same output.The checklist item for DML stays open until
MERGEandON CONFLICTare done.Next: the foreign key checks, then
ON CONFLICT.tamnd commentedon Oct 19, 2026
#397 is merged.
INSERT,UPDATEandDELETEnow check the foreign keys after the statement, as the internal triggers ofri_triggers.cdo. The checks supportMATCH SIMPLE,MATCH FULL,NO ACTIONandRESTRICT, a table that references itself, and keys of two types. The errors and their details are the same as in PostgreSQL. The session reads the other table one time for each foreign key and keeps its keys in a hash map.The actions
CASCADE,SET NULLandSET DEFAULTand the constraints that areINITIALLY DEFERREDstill give0A000. The row locks ofFOR KEY SHAREare not made yet.The checklist item for DML stays open. Next: the actions of the foreign keys, then
ON CONFLICT.682 remaining items
tamnd commentedon Oct 31, 2026
The CI on main failed at cargo xtask style after #1221. This fixes the SAFETY comment in os::StopSignals. (#1225)
tamnd commentedon Oct 31, 2026
#1229 is merged. The planner now makes a
Gather Mergeover aSortof a parallel scan for the order of the query, asgenerate_useful_gather_pathsdoes. WithLIMIT, theSortbelow theGather Mergereads only the first rows, ascreate_ordered_pathsdoes.connection/explain_gather_merge.testhas the plans and the output is the same as PostgreSQL 19.The full schedule timed out after 179 of 239 tests because of the load on server2. In these tests no file got worse.
select_parallelgoes from 553 to 487 diff lines,incremental_sortfrom 373 to 353,subselectfrom 558 to 540 andgroupingsetsfrom 189 to 183 with #1218.explaingoes from 28 to 19.Next in the parallel plans:
create_partial_distinct_paths, so thatSELECT DISTINCTover a parallel scan has aHashAggregateor aUniquein each process below theGather.tamnd commentedon Oct 31, 2026
The catalog_statistics test failed since #1128 and stopped the release of 0.4.15 and 0.4.16. This fixes it. (#1231)
tamnd commentedon Oct 31, 2026
Release 0.4.17 (#1234), with #1225 to #1232. The tag v0.4.16 did not build because of cargo xtask style and catalog_statistics, which #1225 and #1231 fix, so 0.4.17 has the changes of 0.4.16 too.
tamnd commentedon Oct 31, 2026
Commits to a frozen table now go to the commit log instead of a full checkpoint. (#1242)
tamnd commentedon Oct 31, 2026
#1248 is merged. The planner now makes
DISTINCTin two parts over a parallel scan, ascreate_partial_distinct_pathsdoes: aHashAggregateor aUniqueover aSortin each process below aGatheror aGather Merge, and the finalDISTINCTabove it. The plans and the costs inconnection/explain_partial_distinct.testare the same as PostgreSQL 19.The PR also fixes the order of the rows when a partial hash table is below a
Gather Merge.hash_tablesno longer goes below a gather, so aUniqueor aFinalize GroupAggregateover aGather Mergegives its rows in the order of the keys.The full schedule timed out after 225 of 239 tests because of the load on server2, and no file got worse in these tests.
select_distinctgoes from 48 to 20 diff lines andincremental_sortfrom 353 to 342.Next in the parallel plans: a parallel index scan and index only scan, the partial
Limitfor aDISTINCTkey that is a constant, andParallel Append.tamnd commentedon Oct 31, 2026
A logged commit now reads the versions of each changed table once instead of five times. (#1251)
tamnd commentedon Oct 31, 2026
#1253 is merged. When a condition makes each key of
DISTINCTa constant, each process of a parallel scan now has aLimitof one row below theGather, ascreate_partial_distinct_pathsmakes it. The plan ofSELECT DISTINCT four FROM tenk1 WHERE four = 10inselect_distinctis now the same as PostgreSQL 19. The next work is the parallel index scans and index only scans, with therandom_page_costsetting thatselect_paralleluses.tamnd commentedon Nov 1, 2026
PR #1258 is merged. It adds parallel scans of a btree index and
Parallel Appendforselect_parallel.Parallel Index Scanor aParallel Index Only Scan. The number of workers comes from the pages of the heap and of the index, ascompute_parallel_worker.INHERITSor a partitioned table gets aParallel Append, also with children that haveparallel_workersat 0. Withenable_parallel_appendoff, the plan is anAppendof the partial scans.random_page_costis now a setting.Gatherof a base relation has the rows of the relation, as in PostgreSQL 19.The two new tests have the output of PostgreSQL 19. All 900 tests of the
rupg-sessionlibrary pass.Next in
select_parallel:Parallel Bitmap Heap Scan, partial aggregates for aGROUP BYof an expression, and the hashedSubPlanfilter in a parallel scan.tamnd commentedon Nov 1, 2026
PR #1265 is merged. It adds the
Parallel Bitmap Heap Scanforselect_parallel.parallel_safeinadd_path. When a plain plan and aGathercost about the same, the plain plan wins, as in PostgreSQL 19.select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand = 0withenable_seqscanoff is now the same as in PostgreSQL 19: a nested loop of twoGathernodes, with aParallel Bitmap Heap Scanon the inner side.The new test has the output of PostgreSQL 19. All 904 tests of the
rupg-sessionlibrary pass.tamnd commentedon Nov 1, 2026
Merged #1269. A parallel plan can now read an
InitPlanand an uncorrelatedSubPlanwith noGather. A correlatedSubPlankeeps the scan out of the parallel plan. TheOne-Time FilterofEXISTSgoes below thePartial Aggregate, as in PostgreSQL 19.EXPLAINshows a scalar subquery afterANYwith no cast.The new test is
explain_parallel_sublink. The session tests pass (905 tests).Next in
select_parallel: partial aggregates for aGROUP BYexpression, and partial joins below aGather.tamnd commentedon Nov 1, 2026
Merged #1271.
GROUP BYwith no aggregate call now has a plan in two parts, aPartial HashAggregatebelow aGatheror aGroupin each process below aGather Merge. The partial nodes have the cost of the key expressions for each group, so the choice between the plain plan and the plan in two parts is the same as in PostgreSQL 19, with the same costs.The new test is
explain_parallel_group. The session tests pass (908 tests).tamnd commentedon Nov 1, 2026
Asynchronous commit with a WAL writer, log chunks of zeros, and a commit log that reads only the changed versions (#1273)
tamnd commentedon Nov 1, 2026
Less work before a simple statement: settings index, known parameters, search path and type caches, relation maps (#1274)
tamnd commentedon Nov 1, 2026
Merged #1277. A join of a partial outer path now has a partial path, so a
Gathercan be above a nested loop, a merge join or a hash join, and aParallel Hash Joincan read aParallel Hashthat the workers fill together. The inner path follows the rules of PostgreSQL 19: a partial nested loop takes the cheapest total inner path only when a worker can run it, and a merge join or a hash join takes the cheapest inner path that a worker can run. The partial aggregate can read a partial join.The new test is
explain_parallel_join. The session tests pass (910 tests).