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.647 remaining items
tamnd commentedon Oct 30, 2026
Merged #1183. Deparse and
EXPLAIN VERBOSEnow name the temporary schema of the sessionpg_temp, asget_namespace_name_or_tempdoes. AWindowAggbelow anotherWindowAggnow gives its input columns before its window calls, ascreate_one_window_pathdoes. Theexplainregression diff goes from 423 to 386 lines, and thewindow,temp,create_viewandtriggersdiffs do not change.Next:
HAVING falseas aResultnode withReplaces: Aggregate, and the temporary schema made on first use forCREATE FUNCTION pg_temp.f.tamnd commentedon Oct 30, 2026
statement_timeout now stops a statement, and scans, joins, aggregates and the projection check for a cancel after each 1024 rows. (#1184)
tamnd commentedon Oct 30, 2026
Merged #1185.
CREATE FUNCTION,CREATE DOMAIN,CREATE TYPEandCREATE AGGREGATEwith the schemapg_tempnow make the temporary schema of the session when it does not exist yet, asLookupCreationNamespacedoes. Before, they failed withschema "pg_temp" does not existuntil the session made a temporary table. The diffs of 9 related regression tests do not change.Note:
catalog_statisticsinrupg-sessionfails on main, also before #1183. It compares thepg_statsvalues ofsql_parts. That area is in the ANALYZE work.Next:
HAVING falseas aResultnode withReplaces: Aggregatein EXPLAIN.tamnd commentedon Oct 30, 2026
Merged #1186. A query with
HAVINGor grouping sets, but with noGROUP BYitem and no aggregate, now plans ascreate_degenerate_grouping_pathsdoes in PostgreSQL 19: aResultwithReplaces: Aggregateand theHAVINGconditions as its one-time filter, or anAppendof oneResultfor each grouping set. The plan does not readFROM. Theexplainregression diff goes from 386 to 366 lines and thejoindiff from 4750 to 4625 lines. Other related tests do not change.Next: the Query Identifier in
EXPLAIN VERBOSEwithcompute_query_id, then the top "not supported yet" groups (ALTER,CREATE,XMLTABLE).tamnd commentedon Oct 30, 2026
Merged #1187.
EXPLAIN VERBOSEnow showsQuery Identifierafter the plan whencompute_query_idison, as in PostgreSQL 19. Withautoandregressit shows no line, as in PostgreSQL. The identifier is a hash of the tokens of the statement with each constant as the same token, so statements that differ only in their constants share an identifier. The numbers are not the numbers of PostgreSQL, which hashes its own query tree. Theexplainregression diff goes from 363 to 334 lines.Next:
pg_stat_activity.query_idwith the same function, then the top "not supported yet" groups (ALTER,CREATE,XMLTABLE).tamnd commentedon Oct 30, 2026
Merged #1188.
pg_stat_activity.query_idnow shows the query identifier of the running statement whencompute_query_idisonorregress. It uses the same hash asEXPLAIN VERBOSE, so the two values match for the same statement. With the defaultautothe column stays null and rupg computes no hash, so the default path has no extra cost.Next: the top "not supported yet" groups in the regression run (
ALTER,CREATE,XMLTABLE), one category for each PR.tamnd commentedon Oct 30, 2026
Merged #1189. A full
parallel_schedulerun on the current main gives 141 tests that pass and 98 that fail, and it found one crash.EXPLAIN ANALYZEof a nested loop that scans aSortof a removed subquery again stopped the server process with an index out of bounds (select_parallel, withenable_materialoff). The row count of the subquery now starts again at each new scan, and the output is the same as PostgreSQL 19. The PR also fixes therustfmtformat of #1188.The top "not supported yet" groups in this run:
ALTER(112),pg_restore_*_stats(155, the other session's area),CREATE(46),XMLTABLE(36),test_enc_conversion(24), ICU collations (other session),pg_column_compression(22),xpath(20),brin_summarize_range(20),XMLSERIALIZE INDENT(20),DROP(20),TABLESPACE(17). Next: theALTER,CREATEandDROPgroups, split by statement type.tamnd commentedon Oct 30, 2026
Aggregates and function calls use less time for each row: avg of integer keeps a 128-bit sum, and the transition and operator calls need no vectors. (#1190)
tamnd commentedon Oct 30, 2026
Merged #1191: the
TABLESPACEclause ofCREATE TABLE,PARTITION OF,CREATE TABLE AS,CREATE MATERIALIZED VIEW,CREATE INDEXand constraint indexes, withdefault_tablespaceandpg_class.reltablespace.DefineRelation,DefineIndexandGetDefaultTablespaceof PostgreSQL 19: the inherited tablespace of a partition, the check ofCREATEon the tablespace, thepg_globalerror and the error for a partitioned relation in the default tablespace.SET default_tablespacerejects a tablespace that does not exist.DROP TABLESPACEgives "some objects depend on it" for a partitioned relation and "is not empty" for other relations.tablespaceregression diff goes from 633 to 268 lines. The full schedule stays at 141 ok.Next in the tablespace category:
ALTER TABLE/INDEX/MATERIALIZED VIEW ... SET TABLESPACE,ALL IN TABLESPACEandREINDEX (TABLESPACE), thenALTER TABLESPACE.tamnd commentedon Oct 30, 2026
A scan of some of the columns of a table finds the positions of the columns once, and not again for each row. (#1192)
tamnd commentedon Oct 30, 2026
Released 0.4.14 (#1193), with the changes from #1169 to #1192.
tamnd commentedon Oct 30, 2026
Merged #1194:
ALTER TABLE/INDEX/MATERIALIZED VIEW ... SET TABLESPACE,ALTER ... ALL IN TABLESPACEandREINDEX (TABLESPACE).SET TABLESPACEfollowsATPrepSetTableSpaceandATExecSetTableSpace. The TOAST table and its index move with the table and get a newrelfilenode. A secondSET TABLESPACEin one statement is an error.ALL IN TABLESPACEfollowsAlterTableMoveAll, withOWNED BY, the owner check, thepg_globalerror and the "no matching relations" notice.REINDEX (TABLESPACE)moves the rebuilt indexes. System and TOAST relations give "cannot move system relation".pg_globalgives the errors of PostgreSQL 19.REINDEX SCHEMA,SYSTEMandDATABASEskip the catalogs with one warning.tablespaceregression diff goes from 268 to 63 lines. The full schedule stays at 141 ok.Next in the tablespace category:
ALTER TABLESPACE ... SET/RESET/RENAME/OWNERandGRANT ON TABLESPACE.tamnd commentedon Oct 30, 2026
pg_current_wal_lsn, pg_current_wal_insert_lsn and pg_current_wal_flush_lsn now work, which fixes the diff of the sequence regress test. (#1195)
tamnd commentedon Oct 30, 2026
Merged #1196:
ALTER TABLESPACE ... SET/RESET,RENAME TO,OWNER TO, andGRANT/REVOKEon a tablespace.pg_tablespacenow live in the transaction, as the rows ofpg_databasedo. The commit moves them to the store, and a rollback discards them.SET/RESETfollowsAlterTableSpaceOptions.RENAME TOfollowsRenameTableSpace, with the checks of the owner, thepg_prefix, a newline and a duplicate name.OWNER TOfollowsAlterObjectOwner_internaland changes the owner inspcacl.GRANTandREVOKEon a tablespace use the shared ACL code, withCREATEas the only privilege.tablespaceregression test now passes. The full schedule goes from 141 ok to 142 ok, with no test worse.The tablespace category is done, except changes of
pg_defaultandpg_global, which give0A000for now.tamnd commentedon Oct 30, 2026
Merged #1197:
REFRESH MATERIALIZED VIEWnow gives the same errors as PostgreSQL 19 when the new data does not agree with a unique index. A refresh gives the error of the index build for a duplicate key. A concurrent refresh gives the error for duplicate rows without nulls, and checks again for a usable unique index after its query runs. Thematviewregression test now passes.