Repository navigation
Conversation
The noised aggregates drew from one generator per finalize batch (or per vector), seeded from the query seed. Every cell of a query therefore shared the start of the same random sequence: NULL decisions were all-or-nothing across groups (TPC-H Q18: 0 or 57 of 57 NULL per seed) and the noise of all groups fell on the same side of the exact value. A cell's draws also depended on its position in the batch. PacCellRng seeds a generator per cell from hash(seed, key_hash, counters). Cells are now independent, and a cell's release no longer depends on row order, thread or batch. Used by sum, clip_sum, count, min/max, clip_min/max, priv_finalize, as_noised and as_noised_div. Add test/sql/pac_noise_independence.test: NULL rates and noise signs across 1000 groups, and order independence over one and several vectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace per-cell MT initialization and counter hashing with PCG32-backed 64-bit streams. Give each batch a unique stream and advance it across cells. Mark scalar releases volatile and test identical counters across groups and vectors. Draw assignment may vary with execution order.
ila
force-pushed
the
upgrade/duckdb-2.0
branch
from
November 10, 2026 14:05
5da2a75 to
96a264c
Compare
Keep Q20 nation filtering in the outer query, matching the control formulation. Drop Q21 inner orders joins: the outer orders join already provides a matching row for every correlated order key. Both transformations preserve filtered output, including NULL and duplicate-key cases. Verified exact SF30 result equivalence with noise disabled at 1 and 16 threads. Five measured runs at 16 threads cover m=64,128,256,512 on both DuckDB 1.5 and 2.0; Q20 no longer exhausts memory at larger m.
Keep the existing format/tidy checks and tool versions in a local reusable wrapper so dependency installation can avoid the hosted Azure mirror DNS failures. Distribution builds continue to use the pinned upstream workflow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a change record relative to the original paper implementation and experiments, including work already merged into
main. The SUM fixes, SSB/MEPS additions, initial TPC-DS runner, private UNION support, and #37 fix already landed there. This PR introduces the 2.0 port, state spilling, SUM memory/update-adapter changes, PAC random streams, and correlated-plan optimization. Draft #39 contains the larger handwritten TPC-DS benchmark additions.Here a membership mask says which worlds contain a tuple. An aggregate state holds the unfinished results for those worlds within one SQL group, including buffers and separately allocated counter arrays.
SUM bugs: what failed and what changed
Exact integer SUM — fixed on main in #28
These were exposed while validating all SSB world/group answers against ordinary SQL. The arithmetic examples below use
SET pac_mi=0andSET pac_sample_diversity_check=falseto isolate the aggregate from release noise and diversity checks. The fixes are inb7b88e1, with regression cases in pac_sum.test.The exact-build flag did not select the exact algorithm.
PAC_NOAPPROX=ONdefinedPAC_NOAPPROX, but the source selected the algorithm withPAC_EXACTSUM. CMakeLists.txt now maps the option to the macro actually read by the implementation. This matters when interpreting measurements labelled “exact”.Packed signed additions could change another world's counter. Imagine two 8-bit lanes holding
[-1, 0], represented by0x00ff. Adding[1, 0]as one machine-word addition yields0x0100: the intended first-lane result is zero, but the carry also increments the second lane. The fix isolates the high bits so carries cannot cross lane boundaries. The relevant implementation is AddToTotalsSWAR:A signed net total was not a valid bound on each world's subtotal. For example, five pairs of
+30, -30have net sum zero, but a world selecting only the positive rows accumulates 150 and can overflow a signed 8-bit counter. Overflow accounting now tracks input magnitudes rather than cancellation-prone net totals. INT64_MIN's magnitude is formed in unsigned arithmetic; the exact signed path routes it to a wider accumulator. See the update code and exact-state overflow/flush logic.Merging partial exact states repeated the packed-word carry problem. Two worker states were combined by adding packed words. The fix combines the individual lane values after making room at the corresponding level. This is distinct from the per-row update fix. See CombineLevel and PacSumCombineInt.
Finalization could leave contributions behind. A chain of
if / else ifflushed only the first allocated width, even when wider levels also contained values; an intermediate level could also be missing. Finalization now visits populated levels bottom-up and allocates an intermediate level when a pending contribution must pass through it. See Flush8/16/32/64 and Flush.“Exact” describes the integer accumulation path. Returning the aggregate's FLOAT lanes still rounds sufficiently large integers; validation compares at that output precision.
Approximate SUM — loss during cascading/finalization fixed on main
Trigger: SSB Q4.3 had a world with true sum 55,890 but output 53,248, about 4.7% low. A much larger value in a different world caused finalization to push the small world's counter up to a coarse common level. The old cascade also discarded low bits whenever counters moved between levels.
Resolution:
3da3eefretains each counter's low four bits, transfers only its upper bits, sums every level during finalization, and handles a nearly-full source level when merging states.0bc3082preserves those fixes but restores per-input truncation after round-to-nearest added about 2–3% to the measured update hot path. The final implementation truncates inputs; it does not round them to nearest.Simplified pseudocode for one world, from Cascade/GetTotals/CombineFrom:
The identity
x = (x >> 4) * 16 + (x & 15)explains why the transfer is lossless. Finalization no longer forces partially populated levels upward. Separate positive/negative magnitudes are combined afterward in the default signed implementation.This example now produces 55,888, with only the remaining input truncation, even when the other world contains a large value:
The routing code retains approximately 9–12 significant bits and documents a per-input relative truncation bound of 2^-8. This is not the old 0.024% universal bound on a final answer. Cancellation can amplify relative error in a signed sum, and FLOAT result construction contributes output rounding. The paper must describe these distinctions.
Negative-only counters and large magnitudes — fixed on main in #29
Found while cross-checking the PostgreSQL implementation; fixed by
40b15a1, with cases in pac_counters.test.Negative-only worlds disappeared from
as_sum's returned counter list. Finalization subtracted the negative totals through a dummy mask, then discarded worlds absent from the positive mask. It now passes the actual output mask so negative membership is included. See PacSumAvgFinalizeCountersInternal.Unsigned values ≥2^63 were routed as negative signed values.
GetLevel(int64_t)chose the wrong level, arithmetic shifts sign-extended unsigned magnitudes, and negating INT64_MIN in signed arithmetic was undefined. The level function now acceptsuint64_t, unsigned input uses a logical shift, and magnitude formation usesuint64_t(0) - uint64_t(value). The fix covers large UBIGINT inputs and INT64_MIN's magnitude without adding another per-row state structure.Important remaining distinction: the counter-list fix does not fix the fused scalar negative-only SUM/AVG problem in #30. The counter example above is correct, while
as_noised_sum(3::UBIGINT, value)over the same rows still returns NULL. The open-issues section records that gap explicitly.TPC-DS findings and the DuckDB 2.0 CTE port
A CTE is a named intermediate query result. In DuckDB's logical plan it has a definition that produces columns and readers that consume them. Each reader has its own column identifiers and cached types. DuckDB 2.0 also uses generated CTEs internally to turn correlated subqueries into shared computation; this is not limited to SQL that explicitly writes
WITH.For example, a correlated average can conceptually become an intermediate table keyed by part. After stochastic rewriting, its average column may contain 64 world-specific values rather than one scalar. Updating only the definition is insufficient: readers and expressions above them must know that the column is a list, where its world membership comes from, and when the values may be finalized.
The example explains the required propagation; it is not literal generated SQL. The implementation uses the default 2.0 planner rather than enabling the deprecated
delim_join_as_cte=falsemode. Fixes include:Q09-style scalar subqueries inside CASE retained stale scalar types. DuckDB wraps a scalar subquery in logic that checks whether it returned more than one row. Once its aggregate returns world counters, the wrapper's
first(...)/CASE result must carry that list type too.85346c6and the 2.0 port update those types without removing the cardinality error. Regression cases cover different CASE branches and a subquery that genuinely returns multiple rows.CTE boundaries lost mask columns or counter lineage. The old traversal could see the reader without following its definition, and a rewritten producer could have list output while its reader still advertised a scalar. The native port
66c9006traces definitions/readers, propagates needed membership columns, refreshes reader types, and tracks every stochastic input to an expression rather than only the first one. See SyncCTECounterBindings/RewriteBottomUp, counter-source detection, and CTE mask-column propagation. Generated correlation-key deduplication is kept separate from the tuple stream carrying membership; adding a mask to the deduplication key would change the grouping.Q65 exposed AVG being decomposed twice. The AVG rewrite treated an aggregate over a list of world answers like a scalar
(mask, value)AVG and attempted another SUM/COUNT decomposition, causing integer/list binding failures.3e6723drecognizes the one-argument list form and leaves it to the existing list implementation. See pac_avg_rewriter.cpp.Q94 exposed copied readers referencing the wrong CTE. Deep-copying a correlated plan assigned new identifiers to definitions but left a reader referencing the original definition. Later optimization then looked for statistics under the wrong identifier.
3e6723dremaps the reader's definition identifier if that definition belongs to the copied subtree, while preserving references to definitions outside it. See ApplyIndexMapToSubtree.Raw UNION branches needed a shared membership representation. TPC-DS's UNION shapes motivated the mask-carrying/OR-based rewrite below. Branches are not simply counted as supported because they execute; membership propagation and sampled-world semantics are checked separately. Unsupported constructs retain explicit errors.
Q78 exceeded memory despite ordinary DuckDB succeeding. Its high-cardinality, three-SUM-per-group states motivated state spilling plus the late-allocation fixes described below. Issue #36 retains the original failure conditions.
The larger handwritten-query/reference expansion is in draft #39. Its per-world validator currently creates a macro referring to nonexistent
as_stddev_sampeven for queries that do not use STDDEV. Q17 also actually requires a STDDEV rewrite. This remains benchmark work to resolve; it is not claimed as fixed compiler coverage.UNION, UNION ALL, and DISTINCT
Implemented on main in #38 by
35343a4, with additional join-shape guards indb220e2. See PrivateUnionRewriter and independent sampled-world tests.xarrives with001and100, emitx,101, not two distinct(value,mask)rows. In a world containing both original rows,xmust still appear once.Top-level privacy bypass (#37), fixed directly on main: a different entry-point bug skipped all checks when UNION was the outermost operation. For example,
SELECT COUNT(DISTINCT pid) FROM p UNION ALL SELECT 0returned the exact count, andSELECT protected_column FROM t UNION ALL SELECT 'x'exposed protected rows.0a833e8routes top-level UNION through normal checks/rewrite, checks both branches, and follows union columns through outer projection/grouping. The fix is incorporated here, with its 2.0 table-index adaptation in371825e. Issue #37 is closed.Regression coverage checks protected output, both branch positions, wrappers, EXPLAIN, CTAS, and required parameters across the privacy modes. Existing restrictions on DP query shapes remain; multiple branches do not silently receive independent full query budgets. Two old tests relied on the bypass: exact full-data UNION expectations were replaced by independently sampled SQL references, and protected-key fixture construction now explicitly disables rewriting only during setup. See privacy_links.test and the fixture change.
PAC noise: independent draws, seeds, and volatile functions
Reused random sequences — fixed in this PR
While checking grouped PAC releases, we found that restarting generators from the same query seed reused sequences across groups/batches/workers. In one Q18 diagnostic, either all 57 groups became NULL or none did. The related row-filter issue #31 reported 0.929–0.930 agreement between decisions at matching row positions in different scan groups, although their independent decisions should agree roughly half the time.
The stream implementation is in
e64bdc1; per-query resetting is in29c8a3c. See PacRandomState/PacRandomStream, the PCG engine, and categorical local-state initialization.This avoids hashing every cell's 64 counters or initializing a large generator per group. The draw distribution/calibration code remains separate from stream allocation. The change concerns PAC noise and random categorical decisions; it does not replace the DP mechanisms' own noise generator with PCG.
On the current 2.0 build, rerunning #31's four-million-row/eight-thread example gave agreement 0.50007 and 0.50016 at the two reported offsets. That issue remains open on GitHub because this fix is on the unmerged PR branch. Independent noise/NULL-decision and equal-counter regressions are in pac_noise_independence.test.
Seed reproducibility — fixed without requiring stable group identities
Initially, allocating distinct streams without resetting their sequence made subsequent executions differ even with a fixed seed.
29c8a3cresets the stream allocator per query, including prepared executions. Unrelated earlier queries no longer shift later draws.Bit-identical seeded results require the same build, data, settings, execution order, and initial p-tracking state. Tests use one thread and stable ordering. Parallel scheduling can assign draws to different groups even at an unchanged thread count; invariance to row/thread reordering is not promised. See the documented seed contract.
Constant-folded scalar noise — fixed in registration and planning
If DuckDB treats a random function as deterministic,
as_noised(constant_counter_list)can be evaluated once and reused for many output rows. The scalar release functions are registered VOLATILE: each call must be evaluated separately even with equal arguments. The final registration and correlated-plan handling are in895a563.The 4,096-row isolated regression produced distinct scalar outputs for both release functions. P-tracking is disabled in that diagnostic to isolate random-draw assignment from adaptive calibration. These checks detect the implementation defect; they are not a proof of the full privacy mechanism.
TPC-H changes and their motivation
The only final TPC-H SQL edit in this PR is the handwritten AS Q20. AS Q17, AS Q21, the ordinary control SQL, naive SQL, simple-hash SQL, and C++ runner retain their previous text.
Q20: an unnecessarily correlated nation filter.
n_name='CANADA'referred to the outer nation row but appeared inside theINsubquery.f122d3bmoves it to the outer WHERE, matching canonical TPC-H/control SQL. It still selects Canadian suppliers and retains the LINEITEM-to-ORDERS join needed to recover customer membership. The deterministic SF30 output matches all 5,405 reference rows.Q21: too much work finding rows that disqualify an order. The query asks whether other suppliers exist and whether another supplier delivered late. The slow 2.0 plan formed a large candidate set before applying the exclusion. The extension now passes the relevant order-key restriction into the LINEITEM/ORDERS work that finds disqualifying rows, using existing DuckDB execution filters. The restriction is only applied on the side where removing irrelevant candidate rows preserves the NOT EXISTS result. SQL NULL equality, duplicates, output columns, LIMIT barriers, and volatile expressions are checked. All original ORDERS joins remain; an attempted SQL join removal was reverted in
895a563. The SF30 output matches all 100 reference rows. See as_correlation_optimizer.cpp.Q17: retain sharing while evaluating fresh noise per row. Registering scalar noise as VOLATILE correctly prevented draw reuse but also made the new planner repeat deterministic correlated aggregation. The extension separates those operations: compute/reuse counters by the real correlation keys, then evaluate the random release separately for each outer row. Safe brand/container filters still apply before that work. This is implemented in
895a563rather than modifying Q17 SQL. The noise-disabled reference result is 9,076,953.965714285.Grouped SUM adapter overhead. In the first 2.0 adaptation, updating a group created sliced vector wrappers and incurred their reference-management work.
72f8fc5reads the original vectors using existing group row selections directly. This changes the engine adapter, not the per-value arithmetic or state layout. See PacSumUpdateValues/PacSumUpdateClusters.State spilling and the Q78 memory fix
Issue #36 records TPC-DS Q78 exhausting the 32 GB Graviton machine while ordinary DuckDB succeeds. Q78 forms many year/item/customer groups, each with three stochastic sums, then joins the channel aggregates. Two separate memory problems had to be addressed.
1. Make the complete unfinished state spillable
The hash-table entry contains pointers to buffers and counter arrays allocated elsewhere. Writing its pointer bytes does not write those counters, and reading those bytes back does not reconstruct valid ownership. DuckDB 2.0 exposes an aggregate state-export/import interface; the extension implements that interface and lets DuckDB choose and write aggregate partitions.
86050beintroduced SUM transport;3e6723dextends mixed stochastic aggregation. See the shared writer/reader/callbacks, SUM state codec, COUNT codec, and MIN/MAX codec.2. Prevent late expansion of millions of small SUM states
Spilling alone still exhausted a 16 GB DuckDB limit. The later merge/result phases expanded groups containing only one or two buffered inputs into full stochastic states, after earlier spilling decisions had already been made. Writing inactive partitions to disk does not bound all such late allocations.
86050be, retained/scoped by68b575c, keeps small groups buffered when merging and finalizes a temporary copy using storage released after the callback. See CombineSumBuffers/PrepareSumFinalization. The permanent source state no longer grows simply because a result was emitted. These SUM memory changes also apply in memory; COUNT/MIN/MAX retain their existing in-memory update/merge/finalize kernels.With both changes, the tested automatic Q78 SF30 query completed at four threads with a 6 GB DuckDB limit in 18.2 seconds, about 254 MiB peak temporary storage, and all 100 rows matching the independent world-zero SQL reference. This is a managed-memory limit, not a 6 GB process-RSS claim or a guarantee for every thread/memory configuration. The new Graviton SF30 run also completed five measurements for both handwritten and automatic AS at 16 threads (approximately 2.21 s and 2.23 s medians). Its performance summary labels these executions as not numerically validated; successful execution is separate from the independent output checks in the lower-memory diagnostic.
Additional bugs found while testing state restoration
Unaligned state pointers after variable-length aggregates. Fixed by
0b7852d. Astring_aggrestoration can leave the shared arena's next address unaligned. Buffered stochastic states use low pointer bits as metadata and assume an aligned real pointer. For example:Import now calls AlignNext before rebuilding a state. This is pointer-tagging alignment, not alignment of a SIMD load. The regression in pac_sum.test mixes
string_agg(repeat('x',13),'')withas_sum(...), forces external aggregation, and checks all 100,000 groups. It crashed before the fix and passes afterward.NULL imported states lost identity semantics. Fixed by
24b7041. A NULL serialized value must leave the caller's initialized empty state intact: finalizing it remains NULL, while combining it with a real state preserves that real state. Import now does that instead of treating NULL as a normal payload.Windows could select the wrong SUM state layout. Fixed by
1c65642. The linker can merge identical initializer functions, so callback addresses do not uniquely identify integer versus floating layouts. Export/import selection now follows the declared SQL input type in RegisterSumStateExport. Focused MSVC SUM tests pass after this change.Restored small buffers could be overwritten on combine. COUNT/MIN/MAX imports materialize their restored buffers using existing kernels before normal merging; SUM keeps its buffer because its updated combiner handles it. See state-transport documentation and
3e6723d.State regressions also cover signed/unsigned/floating/DECIMAL types, correction arguments, empty/buffered/high-cascade states, combining restored states, and malformed/truncated payloads. An actual-spill checksum is compared with the in-memory result.
Additional workloads: SSB and MEPS
SSB: all 13 joined queries over customer-sampled worlds
Origin and files. The end-to-end SSB benchmark was added in
b7b88e1, merged in #28, and refined in25409d8. The 2.0 compatibility updates are in66c9006ande18010f. Relevant artifacts:q11.sqlthroughq43.sqldenote the standard Q1.1 through Q4.3 names.What the experiment measures. We run all 13 standard SSB queries on the original five tables at SF30. CUSTOMER is the sampling entity, and every LINEORDER row inherits its customer's membership.
priv_hash(hash(lo_custkey))is evaluated inside the timed SQL. Because LINEORDER already has the customer foreign key, obtaining membership does not require an extra CUSTOMER join; queries using customer attributes still retain that join. The original joins, predicates, grouping keys, and ORDER BY clauses remain. Depending on the query, one to four original joins execute.The fixed configuration is
privacy_seed=42,pac_mi=0, andpac_hash_repair=true. Each customer appears in exactly 32 of 64 overlapping worlds, and all its fact rows use the same mask. These are customer-level subsets added to SSB for this experiment; they are not an independent Bernoulli sample for each world or part of the original SSB specification. PART, SUPPLIER, and DATE remain shared. All timed variants read the base tables; neither a prejoined fact table nor a precomputed membership table supplies the AS query.Baselines and timing. The runner compares ordinary full-data DuckDB, repeated sampled-world SQL, and one AS query returning all 64 worlds. Full-data DuckDB produces a different amount of work and is a single-query cost reference. The repeated-SQL time is the latency of world zero multiplied by 64, following the TPC-H convention; all other worlds execute for validation but their latencies are not added to the reported bar. AS shares the relational work and maintains 64 aggregate answers per group.
Timing includes planning, scans, membership hashing/world filtering, the original joins, aggregation, sorting, and result materialization. Data generation, import, C++ answer conversion, and correctness checks are outside the timer. Each mode has one excluded warmup and five measured runs, with rotating measurement order; figures use per-query medians. The current Graviton run uses 16 threads and a 30 GB DuckDB memory limit. Approximate and exact SUM builds run separately. The exact run measures AS only and reuses ordinary baseline timings, while still executing its independent world references for validation. The wrapper restores the default approximate build afterward.
Correctness and numerical error. Every handwritten AS query is checked against ordinary SQL for every world and group.
as_countdistinguishes an absent group from a present group whose sum is zero. NULLs, missing/extra groups, and non-finite results are checked. Since the aggregate API scales selected sums by two, validation divides AS sums by two before comparison. Exact SUM must agree after converting the reference to the AS output's FLOAT precision. Approximate SUM records maximum/mean normalized error, maximum absolute error, and output-rounding mismatches; it does not hide numerical discrepancies behind a pass tolerance. Normalized error isabs(actual-reference)/max(1,abs(reference)).Automatic rewriting/execution of the 13 original queries is audited separately from correctness of the handwritten SQL. AS orders list-valued aggregate keys lexicographically; validation compares per-world groups and values rather than claiming an independently sorted output stream for every world. SSB has no LIMIT. Saved artifacts include raw timings, coverage status, the executed SQL, EXPLAIN plans, build settings, and data-generation provenance/checksums. The SSB validation is also what exposed the exact-SUM bugs and sparse-world approximation error described below.
MEPS: published survey analyses with official replicate designs
Commits and files. The published-workload MEPS benchmark is implemented in
25409d8. Its runner and SQL receive the DuckDB 2.0 API/lambda updates linked above. Relevant artifacts:Why these data and queries. The goal is to evaluate AS on actual repeated-subset analyses, including a person-to-event join, rather than on a synthetic array reduction. The timed implementation is C++/DuckDB throughout. Python downloads/converts input files before the benchmark; no R interpreter or package runtime is included in the performance comparison.
damico2006age > 17) and nonelderly adults (17 < age < 65)ahrq2016The first four AHRQ queries return weighted event counts and mean expenditure by five payment sources. The visits query returns office visits and physician-office visits per person and retains persons with zero visits through a left join. The 2016 person input is checked against its expected 34,655 records, including 6,933 zero-expenditure people and 33,259 positive-weight people. Preparation verifies person uniqueness, person/panel BRR linkage, event ownership, and binary/non-NULL replicate indicators. Recorded download URLs, hashes, and row counts identify the inputs. The exact pandas XPORT zero-decoding artifact
2**-260is corrected before analysis, preserving real zeros without thresholding other small values.Statistical computation. These are fixed, published replicate memberships, not newly generated random worlds. Under the official MEPS BRR rule, replicate
ruses weight2 * BRR_r * person_weight. Every dependent event inherits its person's flag and survey weight. A replicate mean is its weighted numerator divided by its own weighted denominator. The AS SUM API's factor of two supplies the BRR scaling for weighted counts and cancels between the numerator and denominator for means. The runner uses all 64 or 128 replicate answers to compute BRR standard errors, including both full-estimate-centered and replicate-mean-centered forms. No privacy noise or new sampling procedure is added to this application workload.The Damico workload reproduces the published mean/SE examples and checks their one-decimal values. AHRQ's original 2016 example uses Taylor variance estimation; our adaptation retains its estimands and filters but calculates BRR standard errors using the official replicate design. Matching the original Taylor standard errors is therefore not the claim.
Four methods. Each workload has ordinary SQL in
queries/, a direct-replicate template innaive_queries/, a shared ordinary-SQL implementation inconditional_queries/, and a handwritten AS version inas_queries/:naive_as): read the actualBRR_rflag and evaluate the query with2 * weight * BRR_r. Its baseline is not burdened with constructing AS bitmasks. Reported time is the first replicate's query latency multiplied by 64 or 128; the remaining replicates execute to validate outputs and calculate standard errors.as_sumblocks. A non-materialized view joins people to the BRR file and builds masks inside the query. Per-replicate ratios are computed lane-wise in SQL. Event joins and the visits-per-person aggregation also remain inside the query.Timing and validation.
query_mscovers planning, scans, person/replicate linkage, mask construction where applicable, event joins, weighting, aggregate updates, SQL ratios, and result materialization. Download/conversion, import, initial input validation, query-text generation, EXPLAIN, C++ answer checking, and the final standard-error calculation across replicate outputs are outside this timer. Import and input-validation times are reported separately. Thus this measures end-to-end database query execution, not download-to-analysis wall time.The wrapper runs both SUM builds with one excluded warmup and five measured runs, rotating method order; the Graviton configuration uses 16 threads. It restores the approximate default afterward. Figures report medians for each query/workload/build and use one set of ordinary-control bars. Warmup, raw observations, SQL, plans, build caches, all replicate estimates, and point/SE summaries are retained.
Ordinary repeated SQL supplies the replicate reference. Conditional and AS outputs are compared metric by metric, replicate by replicate, and again after calculating BRR standard errors. Outputs must be finite and have the correct number of metrics/replicates; an undefined ratio is an error rather than a silently dropped replicate. The runner records maximum normalized error and retains the existing guards of
1e-8for ordinary SQL and1e-3for AS. These are validation thresholds, not claimed measured errors or exact-equality guarantees. Damico's published values add an external numerical check; agreement among our three implementations alone is not an independent oracle for the 2016 query translation.Known bugs and unfinished work
These statuses distinguish an issue being open on GitHub from a defect still present in the current PR build. The relevant examples were rechecked while updating this description.
as_sumcounter-list bug was fixed in Fix as_sum counters losing negative-only worlds and magnitudes >= 2^63 #29, butas_noised_sumstill returns NULL if no positive state was allocated. Its early NULL decision also uses positive membership before negative membership is merged. Clipped scalar SUM has the related ordering problem. Thus the counter-list example returning[-24,-24]above does not establish correctness of every scalar SUM/AVG release. See PacSumFinalize and clipped SUM finalization.max(0,-5)returns zero. The current build still returns zero in the issue's negative-only example; ordinaryas_maxis a different path. See as_clip_min_max.cpp.CI dependency setup
The format/tidy jobs repeatedly failed during Ubuntu dependency installation, before either check ran. The quality wrapper in
d39f0b1andfb3d274now uses the hosted image's existing Ninja and clang-tidy 18, installs the pinned clang-format 11/Python check tools through pip, and still runsmake format-checkandmake tidy-check. The official runner manifest lists the preinstalled tools. See CodeQuality.yml. Distribution builds retain the upstream pinned workflow. This is infrastructure maintenance rather than a change to the paper's algorithms.What needs to be changed in the paper
2dc137band the retained implementation in4f06701.This section is a checklist for the paper source; this PR does not edit the Overleaf manuscript.