Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7487942
Draw each released cell's noise and NULL decision from its own generator
ila Oct 30, 2026
e64bdc1
Use lightweight independent noise streams per finalization batch
ila Oct 30, 2026
29c8a3c
Reset PAC noise streams per query for seeded reproducibility
ila Oct 30, 2026
66c9006
Port privacy to DuckDB 2.0 with native CTE decorrelation
ila Nov 1, 2026
b3a55c3
Fix Windows linkage and migrate deprecated DuckDB 2.0 APIs
ila Nov 1, 2026
86050be
Keep small SUM groups buffered and enable native state spilling
ila Nov 4, 2026
fa15070
Document stochastic SUM spill limitations
ila Nov 4, 2026
3e6723d
Fix nested AVG and copied CTEs and spill mixed stochastic aggregates
ila Nov 5, 2026
68b575c
Scope spill follow-up to state transport and retain SUM memory optimi…
ila Nov 5, 2026
0b7852d
Align imported stochastic states after variable-size aggregate alloca…
ila Nov 5, 2026
9534f62
Execute Windows regression tests with statement-level crash tracing
ila Nov 5, 2026
24b7041
Preserve NULL identity semantics when importing stochastic aggregate …
ila Nov 5, 2026
4553220
Retain native Windows test diagnostics during crash investigation
ila Nov 5, 2026
39bdeac
Trace the SUM suite identified by the native Windows crash report
ila Nov 5, 2026
1c65642
Select SUM spill codecs by input type
ila Nov 5, 2026
1903bf7
Document SUM spill codec selection
ila Nov 5, 2026
2bac886
Restore full CI after Windows spill diagnosis
ila Nov 5, 2026
d93abce
Port main UNION and scalar-subquery checks to DuckDB 2.0
ila Nov 10, 2026
96a264c
Port existing handwritten TPC-DS benchmark and validation SQL to Duck…
ila Nov 10, 2026
0b57cee
Add sequential Graviton revision rerun and preserve raw measurements
ila Nov 10, 2026
69a1d6e
Wait for preparation dependencies before converting MEPS inputs
ila Nov 10, 2026
43146f8
Audit revision CSVs against matched and archived analytical results
ila Nov 10, 2026
b53a1c0
Wait for retried dataset preparation before timing benchmarks
ila Nov 10, 2026
e18010f
Update SSB and MEPS setup for DuckDB 2.0 and queue analytical retries
ila Nov 10, 2026
2dc137b
Fix microbenchmark profiling filenames and DuckDB 2.0 JSON parsing
ila Nov 10, 2026
72f8fc5
Update clustered SUM directly from input selections
ila Nov 10, 2026
f122d3b
Avoid redundant correlation and joins in TPC-H AS queries
ila Nov 11, 2026
895a563
Preserve scalar noise independence and optimize AS correlation plans
ila Nov 11, 2026
4f06701
Separate TPC-DS benchmark additions and remove one-off rerun tooling
ila Nov 11, 2026
fafe2a0
Merge main UNION privacy checks into the DuckDB 2.0 upgrade
ila Nov 11, 2026
371825e
Adapt UNION source lookup to DuckDB 2.0 table indices
ila Nov 11, 2026
d39f0b1
Run quality checks against the official Ubuntu package mirror
ila Nov 11, 2026
fb3d274
Use hosted Ninja and clang-tidy to avoid redundant apt installs
ila Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/CodeQuality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Code Quality

on:
workflow_call:

jobs:
check:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- name: Format Check
runner: ubuntu-22.04
pip_packages: cmake-format 'black==24.*' cxxheaderparser pcpp 'clang_format==11.0.1'
pip_flags: ''
tidy_binary: ''
target: format-check
- name: Tidy Check
runner: ubuntu-24.04
pip_packages: '"pybind11[global]"'
pip_flags: --break-system-packages
tidy_binary: clang-tidy-18
target: tidy-check
env:
CC: gcc
CXX: g++
GEN: ninja
TIDY_THREADS: 4
TIDY_BINARY: ${{ matrix.tidy_binary }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
submodules: recursive

- name: Install Python check tools
shell: bash
run: |
# Ninja and clang-tidy-18 are already on the hosted Ubuntu image.
# Avoid redundant apt installs that fail on the image's package mirrors.
sudo pip3 install ${{ matrix.pip_packages }} ${{ matrix.pip_flags }}

- name: Run check
run: |
if [ '${{ matrix.target }}' = 'tidy-check' ]; then
ninja --version
"$TIDY_BINARY" --version
fi
make ${{ matrix.target }}
13 changes: 4 additions & 9 deletions .github/workflows/MainDistributionPipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@ concurrency:
jobs:
duckdb-stable-build:
name: Build extension binaries
uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.5.4
uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@ebdcc1961780b0ce1272a0d7637dc58447c34885
with:
duckdb_version: v1.5.4
ci_tools_version: v1.5.4
duckdb_version: v2.0.0
ci_tools_version: ebdcc1961780b0ce1272a0d7637dc58447c34885
extension_name: privacy

code-quality-check:
name: Code Quality Check
uses: duckdb/extension-ci-tools/.github/workflows/_extension_code_quality.yml@v1.5.4
with:
duckdb_version: v1.5.4
ci_tools_version: v1.5.4
extension_name: privacy
format_checks: 'format;tidy'
uses: ./.github/workflows/CodeQuality.yml
165 changes: 83 additions & 82 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ cmake_minimum_required(VERSION 3.5)
# Set extension name here
set(TARGET_NAME privacy)

# DuckDB's extension distribution supports vcpkg. As such, dependencies can be added in ./vcpkg.json and then
# used in cmake with find_package. Feel free to remove or replace with other dependencies.
# Note that it should also be removed from vcpkg.json to prevent needlessly installing it..
#find_package(OpensSL REQUIRED)
# DuckDB's extension distribution supports vcpkg. As such, dependencies can be
# added in ./vcpkg.json and then used in cmake with find_package. Feel free to
# remove or replace with other dependencies. Note that it should also be removed
# from vcpkg.json to prevent needlessly installing it.. find_package(OpensSL
# REQUIRED)

set(EXTENSION_NAME ${TARGET_NAME}_extension)
set(LOADABLE_EXTENSION_NAME ${TARGET_NAME}_loadable_extension)
Expand All @@ -15,15 +16,16 @@ project(${TARGET_NAME})
include_directories(src/include)
include_directories(benchmark/include)

# Option to disable approximate sum algorithm (use exact cascading instead)
# By default, pac_sum/pac_avg use the approx algorithm (16-bit cascading counters)
# Option to disable approximate sum algorithm (use exact cascading instead) By
# default, pac_sum/pac_avg use the approx algorithm (16-bit cascading counters)
# Set PAC_NOAPPROX=ON to use exact cascading instead
option(PAC_NOAPPROX "Disable approximate sum algorithm, use exact cascading" OFF)
option(PAC_NOAPPROX "Disable approximate sum algorithm, use exact cascading"
OFF)
if(PAC_NOAPPROX)
add_definitions(-DPAC_EXACTSUM)
message(STATUS "privacy: Using exact cascading sum algorithm (PAC_NOAPPROX)")
add_definitions(-DPAC_EXACTSUM)
message(STATUS "privacy: Using exact cascading sum algorithm (PAC_NOAPPROX)")
else()
message(STATUS "privacy: Using approximate sum algorithm (default)")
message(STATUS "privacy: Using approximate sum algorithm (default)")
endif()

# Extension sources
Expand All @@ -37,6 +39,7 @@ set(EXTENSION_SOURCES
src/aggregates/as_count.cpp
src/aggregates/as_min_max.cpp
src/aggregates/as_sum.cpp
src/aggregates/as_sum_state_export.cpp
src/aggregates/as_finalize.cpp
src/aggregates/as_clip_sum.cpp
src/aggregates/as_clip_min_max.cpp
Expand All @@ -48,6 +51,7 @@ set(EXTENSION_SOURCES
src/compiler/privacy_mechanisms.cpp
src/compiler/privacy_compiler_helpers.cpp
src/query_processing/pac_avg_rewriter.cpp
src/query_processing/as_correlation_optimizer.cpp
src/query_processing/privacy_expression_builder.cpp
src/query_processing/privacy_plan_traversal.cpp
src/query_processing/pac_projection_propagation.cpp
Expand All @@ -69,8 +73,8 @@ build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES})
build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES})

if(WIN32)
target_link_libraries(${EXTENSION_NAME} bcrypt)
target_link_libraries(${LOADABLE_EXTENSION_NAME} bcrypt)
target_link_libraries(${EXTENSION_NAME} bcrypt)
target_link_libraries(${LOADABLE_EXTENSION_NAME} bcrypt)
endif()

install(
Expand All @@ -79,76 +83,73 @@ install(
LIBRARY DESTINATION "${INSTALL_LIB_DIR}"
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}")

# Test runners and benchmarks - only build for native platforms, not WASM or Windows
# (Windows lacks unistd.h and other POSIX features used by the benchmark code)
set(PRIVACY_LINK_LIBS
duckdb_static
duckdb_generated_extension_loader
${EXTENSION_NAME}
core_functions_extension
parquet_extension
duckdb_static)

# jemalloc is only built on Linux 64-bit; link it when the extension was loaded
# (can't use if(TARGET ...) here because the target isn't defined yet at this point)
if(DUCKDB_EXTENSION_JEMALLOC_SHOULD_LINK)
list(APPEND PRIVACY_LINK_LIBS jemalloc_extension)
endif()
# Test runners and benchmarks - only build for native platforms, not WASM or
# Windows (Windows lacks unistd.h and other POSIX features used by the benchmark
# code)

if(NOT EMSCRIPTEN AND NOT WIN32)
# Build a single unified test executable that invokes all tests via src/test/test_runner.cpp
add_executable(pac_test_runner
src/test/test_runner.cpp
src/test/test_plan_traversal.cpp
src/test/test_schema_metadata.cpp
src/test/test_compiler_functions.cpp
src/test/test_privacy_parser.cpp)
target_link_libraries(pac_test_runner ${PRIVACY_LINK_LIBS})

# TPCH + AS benchmark runner
add_executable(as_tpch_benchmark benchmark/tpch/as_tpch_benchmark.cpp)
target_link_libraries(as_tpch_benchmark ${PRIVACY_LINK_LIBS})

# WIP rebuttal experiment: four-way MEPS balanced repeated replication benchmark
add_executable(meps_brr_poc benchmark/meps_brr/meps_brr_poc.cpp)
target_link_libraries(meps_brr_poc ${PRIVACY_LINK_LIBS})

# WIP rebuttal experiment: standard joined SSB queries and 64 sampled worlds
add_executable(as_ssb_benchmark benchmark/ssb/as_ssb_benchmark.cpp)
target_link_libraries(as_ssb_benchmark ${PRIVACY_LINK_LIBS})

# ClickBench + AS benchmark runner
add_executable(as_clickbench_benchmark benchmark/clickbench/as_clickbench_benchmark.cpp)
target_link_libraries(as_clickbench_benchmark ${PRIVACY_LINK_LIBS})

# TPC-DS automatic-rewriter coverage and end-to-end performance
add_executable(as_tpcds_benchmark benchmark/tpcds/as_tpcds_benchmark.cpp)
target_link_libraries(as_tpcds_benchmark ${PRIVACY_LINK_LIBS})

# Unified DP utility benchmark runner
set(TPCH_STOCK_DP_QUERY_NAMES q01 q05 q06 q14 q19)
set(TPCH_STOCK_DP_QUERY_FILES)
foreach(QUERY_NAME IN LISTS TPCH_STOCK_DP_QUERY_NAMES)
string(TOUPPER ${QUERY_NAME} QUERY_NAME_UPPER)
set(QUERY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/benchmark/dp/tpch_queries/${QUERY_NAME}.sql")
list(APPEND TPCH_STOCK_DP_QUERY_FILES "${QUERY_FILE}")
file(READ
"${QUERY_FILE}"
"TPCH_${QUERY_NAME_UPPER}_SQL")
endforeach()
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
${TPCH_STOCK_DP_QUERY_FILES})
set(TPCH_QUERY_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
file(MAKE_DIRECTORY "${TPCH_QUERY_GENERATED_DIR}")
configure_file(
benchmark/include/tpch_stock_queries.hpp.in
"${TPCH_QUERY_GENERATED_DIR}/tpch_stock_queries.hpp"
@ONLY)
add_executable(dp_benchmark_runner benchmark/dp/dp_benchmark_runner.cpp)
target_include_directories(dp_benchmark_runner PRIVATE "${TPCH_QUERY_GENERATED_DIR}")
target_link_libraries(dp_benchmark_runner ${PRIVACY_LINK_LIBS})

# IMDb/JOB benchmark runner
add_executable(imdb_benchmark benchmark/imdb/imdb_benchmark.cpp)
target_link_libraries(imdb_benchmark ${PRIVACY_LINK_LIBS})
# Build a single unified test executable that invokes all tests via
# src/test/test_runner.cpp
add_executable(
pac_test_runner
src/test/test_runner.cpp src/test/test_plan_traversal.cpp
src/test/test_schema_metadata.cpp src/test/test_compiler_functions.cpp
src/test/test_privacy_parser.cpp)

# TPCH + AS benchmark runner
add_executable(as_tpch_benchmark benchmark/tpch/as_tpch_benchmark.cpp)

# WIP rebuttal experiment: four-way MEPS balanced repeated replication
# benchmark
add_executable(meps_brr_poc benchmark/meps_brr/meps_brr_poc.cpp)

# WIP rebuttal experiment: standard joined SSB queries and 64 sampled worlds
add_executable(as_ssb_benchmark benchmark/ssb/as_ssb_benchmark.cpp)

# ClickBench + AS benchmark runner
add_executable(as_clickbench_benchmark
benchmark/clickbench/as_clickbench_benchmark.cpp)

# TPC-DS automatic-rewriter coverage and end-to-end performance
add_executable(as_tpcds_benchmark benchmark/tpcds/as_tpcds_benchmark.cpp)

# Unified DP utility benchmark runner
set(TPCH_STOCK_DP_QUERY_NAMES q01 q05 q06 q14 q19)
set(TPCH_STOCK_DP_QUERY_FILES)
foreach(QUERY_NAME IN LISTS TPCH_STOCK_DP_QUERY_NAMES)
string(TOUPPER ${QUERY_NAME} QUERY_NAME_UPPER)
set(QUERY_FILE
"${CMAKE_CURRENT_SOURCE_DIR}/benchmark/dp/tpch_queries/${QUERY_NAME}.sql"
)
list(APPEND TPCH_STOCK_DP_QUERY_FILES "${QUERY_FILE}")
file(READ "${QUERY_FILE}" "TPCH_${QUERY_NAME_UPPER}_SQL")
endforeach()
set_property(
DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS ${TPCH_STOCK_DP_QUERY_FILES})
set(TPCH_QUERY_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
file(MAKE_DIRECTORY "${TPCH_QUERY_GENERATED_DIR}")
configure_file(benchmark/include/tpch_stock_queries.hpp.in
"${TPCH_QUERY_GENERATED_DIR}/tpch_stock_queries.hpp" @ONLY)
add_executable(dp_benchmark_runner benchmark/dp/dp_benchmark_runner.cpp)
target_include_directories(dp_benchmark_runner
PRIVATE "${TPCH_QUERY_GENERATED_DIR}")

# IMDb/JOB benchmark runner
add_executable(imdb_benchmark benchmark/imdb/imdb_benchmark.cpp)

foreach(
PRIVACY_RUNNER IN
ITEMS pac_test_runner
as_tpch_benchmark
meps_brr_poc
as_ssb_benchmark
as_clickbench_benchmark
as_tpcds_benchmark
dp_benchmark_runner
imdb_benchmark)
target_link_libraries(${PRIVACY_RUNNER} PRIVATE duckdb_static)
link_extension_libraries(${PRIVACY_RUNNER} PRIVATE)
endforeach()
endif()
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PROJ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# Configuration of extension
EXT_NAME=privacy
EXT_CONFIG=${PROJ_DIR}extension_config.cmake
OVERRIDE_GIT_DESCRIBE ?= v2.0.0

# Include the Makefile from extension-ci-tools
include extension-ci-tools/makefiles/duckdb_extension.Makefile
include extension-ci-tools/makefiles/duckdb_extension.Makefile
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Both modes share the same DDL and require no changes to query syntax — the extension rewrites aggregate query plans transparently.

This works on DuckDB v1.5 and beyond. See https://duckdb.org/install to install. Or if you do not want to install anything: this extension is also distributed in WASM, so you can run the examples also in https://shell.duckdb.org in a browser.
This branch targets DuckDB v2.0.0. See https://duckdb.org/install to install. Or if you do not want to install anything: this extension is also distributed in WASM, so you can run the examples also in https://shell.duckdb.org in a browser.

## Install

Expand Down Expand Up @@ -218,6 +218,19 @@ This gives a formal (ε,δ)-DP guarantee at the **user level** — neighboring d
| `dp_delta` | `1e-6` | dp_elastic | Failure probability δ (0 = pure DP) |
| `dp_sum_bound` | required | dp_elastic | Clipping bound for `SUM`/`AVG` inputs: values are clipped to `[-bound, bound]` |

For PAC, a fixed `privacy_seed` repeats the random draws when the query, data,
settings, execution order, and initial p-tracking state are unchanged on the same
build. Stream allocation resets for every query, including prepared executions,
so unrelated queries do not shift later seeded draws. Streams advance across cells:
identical aggregate counters in different groups still receive independent draws.

For reproducible tests, use `SET threads = 1` and a stable input order. Parallel
hash aggregation can emit groups in a different order even at a fixed thread count,
so bit-identical per-group results are not guaranteed across parallel executions,
row/batch reordering, or builds. P-tracking updates its calibration state after
releases; comparisons must start from the same state (the RNG regression tests use
`SET pac_ptracking = false`).

## SQL Reference

### Supported Aggregates and Operators
Expand Down
20 changes: 10 additions & 10 deletions benchmark/meps_brr/ahrq2016/as_queries/q01_office.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
SELECT as_sum(p.mask_lo, p.weight) AS event_count_lo,
as_sum(p.mask_hi, p.weight) AS event_count_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.self), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_self_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_self_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.self), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_self_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_self_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.private), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_private_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_private_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.private), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_private_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_private_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.medicare), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicare_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicare_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.medicare), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicare_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicare_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.medicaid), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicaid_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicaid_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.medicaid), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicaid_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicaid_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.other), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_other_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_other_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.other), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_other_hi
lambda x: x[1] / nullif(x[2], 0)) AS mean_other_hi
FROM persons_as p JOIN events e USING (person_id)
WHERE e.kind = 'OB';
20 changes: 10 additions & 10 deletions benchmark/meps_brr/ahrq2016/as_queries/q02_office_physician.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
SELECT as_sum(p.mask_lo, p.weight) AS event_count_lo,
as_sum(p.mask_hi, p.weight) AS event_count_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.self), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_self_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_self_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.self), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_self_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_self_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.private), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_private_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_private_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.private), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_private_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_private_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.medicare), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicare_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicare_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.medicare), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicare_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicare_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.medicaid), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicaid_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicaid_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.medicaid), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_medicaid_hi,
lambda x: x[1] / nullif(x[2], 0)) AS mean_medicaid_hi,
list_transform(list_zip(as_sum(p.mask_lo, p.weight * e.other), as_sum(p.mask_lo, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_other_lo,
lambda x: x[1] / nullif(x[2], 0)) AS mean_other_lo,
list_transform(list_zip(as_sum(p.mask_hi, p.weight * e.other), as_sum(p.mask_hi, p.weight)),
x -> x[1] / nullif(x[2], 0)) AS mean_other_hi
lambda x: x[1] / nullif(x[2], 0)) AS mean_other_hi
FROM persons_as p JOIN events e USING (person_id)
WHERE e.kind = 'OB' AND e.physician = 1;
Loading
Loading