sqlbenchdag 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrastructure/debug_aws.py +40 -0
- scripts/dev/backfill_queries.py +44 -0
- scripts/dev/timestamp_capsule.py +49 -0
- scripts/dev/upgrade_capsule.py +63 -0
- scripts/dev/verify_capsule.py +79 -0
- scripts/dev/verify_portability.py +103 -0
- scripts/tools/_plotlib.py +43 -0
- scripts/tools/analyze_scaling.py +39 -0
- scripts/tools/demo_breach_detection.py +61 -0
- scripts/tools/extract_results.py +57 -0
- scripts/tools/find_kink.py +104 -0
- scripts/tools/gen_experiment_catalog.py +103 -0
- scripts/tools/load_parquet_to_local.py +98 -0
- scripts/tools/plot_execution_modes.py +82 -0
- scripts/tools/plot_scaling.py +91 -0
- scripts/tools/plot_selectivity.py +139 -0
- scripts/tools/plot_threads.py +95 -0
- scripts/tools/regenerate_dashboard.py +58 -0
- scripts/tools/verify_doc_claims.py +88 -0
- sql_benchmarks/__init__.py +3 -0
- sql_benchmarks/api/__init__.py +0 -0
- sql_benchmarks/api/app.py +30 -0
- sql_benchmarks/api/data/__init__.py +0 -0
- sql_benchmarks/api/data/catalog_reader.py +73 -0
- sql_benchmarks/api/data/reader.py +125 -0
- sql_benchmarks/api/logic/__init__.py +0 -0
- sql_benchmarks/api/logic/comparator.py +44 -0
- sql_benchmarks/api/logic/ranker.py +127 -0
- sql_benchmarks/api/models/__init__.py +0 -0
- sql_benchmarks/api/models/catalog.py +22 -0
- sql_benchmarks/api/models/experiments.py +20 -0
- sql_benchmarks/api/models/recommend.py +11 -0
- sql_benchmarks/api/models/results.py +66 -0
- sql_benchmarks/api/routers/__init__.py +0 -0
- sql_benchmarks/api/routers/catalog.py +19 -0
- sql_benchmarks/api/routers/experiments.py +79 -0
- sql_benchmarks/api/routers/recommend.py +22 -0
- sql_benchmarks/api/routers/results.py +51 -0
- sql_benchmarks/assets/__init__.py +4 -0
- sql_benchmarks/assets/benchmark_factory.py +222 -0
- sql_benchmarks/assets/data_factory.py +54 -0
- sql_benchmarks/assets/data_quality.py +91 -0
- sql_benchmarks/assets/ingestion_factory.py +88 -0
- sql_benchmarks/assets/maintenance.py +63 -0
- sql_benchmarks/assets/reporting.py +385 -0
- sql_benchmarks/assets/semantic_gate.py +76 -0
- sql_benchmarks/cli.py +69 -0
- sql_benchmarks/config_loader.py +104 -0
- sql_benchmarks/constants.py +60 -0
- sql_benchmarks/coordinator.py +282 -0
- sql_benchmarks/definitions.py +115 -0
- sql_benchmarks/experiments/templates/experiment_template.yaml +99 -0
- sql_benchmarks/harness.py +84 -0
- sql_benchmarks/jobs.py +8 -0
- sql_benchmarks/partitions.py +17 -0
- sql_benchmarks/plugins/data_sources/declarative_gen.py +211 -0
- sql_benchmarks/plugins/data_sources/local_file_loader.py +62 -0
- sql_benchmarks/plugins/data_sources/tpc_h.py +61 -0
- sql_benchmarks/resources/__init__.py +0 -0
- sql_benchmarks/resources/actian.py +234 -0
- sql_benchmarks/resources/actian_client.py +231 -0
- sql_benchmarks/resources/base_engine.py +58 -0
- sql_benchmarks/resources/base_schema_client.py +104 -0
- sql_benchmarks/resources/duckdb.py +58 -0
- sql_benchmarks/resources/duckdb_client.py +157 -0
- sql_benchmarks/resources/postgres.py +188 -0
- sql_benchmarks/resources/postgres_client.py +184 -0
- sql_benchmarks/resources/quack.py +56 -0
- sql_benchmarks/resources/quack_client.py +169 -0
- sql_benchmarks/resources/typedb_client.py +370 -0
- sql_benchmarks/resources/typedb_engine.py +303 -0
- sql_benchmarks/scripts/__init__.py +0 -0
- sql_benchmarks/scripts/sql/acid_test/duckdb/test.sql +1 -0
- sql_benchmarks/scripts/sql/acid_test/postgres/test.sql +1 -0
- sql_benchmarks/scripts/sql/analytical_wall/actian/analytical_wall.sql +15 -0
- sql_benchmarks/scripts/sql/analytical_wall/analytical_wall.sql.template +19 -0
- sql_benchmarks/scripts/sql/analytical_wall/duckdb/analytical_wall.sql +15 -0
- sql_benchmarks/scripts/sql/analytical_wall/postgres/analytical_wall.sql +15 -0
- sql_benchmarks/scripts/sql/group_by/duckdb/groupby_antipattern.sql +11 -0
- sql_benchmarks/scripts/sql/group_by/duckdb/groupby_recommended_cte.sql +19 -0
- sql_benchmarks/scripts/sql/group_by/postgres/groupby_antipattern.sql +13 -0
- sql_benchmarks/scripts/sql/group_by/postgres/groupby_recommended_pk.sql +11 -0
- sql_benchmarks/scripts/sql/hypergraph/duckdb/q_1hop.sql +5 -0
- sql_benchmarks/scripts/sql/hypergraph/duckdb/q_2hop.sql +6 -0
- sql_benchmarks/scripts/sql/hypergraph/duckdb/q_full.sql +9 -0
- sql_benchmarks/scripts/sql/hypergraph/typedb/q_1hop.sql +4 -0
- sql_benchmarks/scripts/sql/hypergraph/typedb/q_2hop.sql +5 -0
- sql_benchmarks/scripts/sql/hypergraph/typedb/q_full.sql +6 -0
- sql_benchmarks/scripts/sql/joins/duckdb/join_antipattern.sql +5 -0
- sql_benchmarks/scripts/sql/joins/duckdb/join_recommended.sql +4 -0
- sql_benchmarks/scripts/sql/joins/postgres/join_antipattern.sql +5 -0
- sql_benchmarks/scripts/sql/joins/postgres/join_recommended.sql +4 -0
- sql_benchmarks/scripts/sql/null_logic/duckdb/2VL_identity.sql +14 -0
- sql_benchmarks/scripts/sql/null_logic/duckdb/3VL_identity.sql +5 -0
- sql_benchmarks/scripts/sql/null_logic/postgres/2VL_identity.sql +14 -0
- sql_benchmarks/scripts/sql/null_logic/postgres/3VL_identity.sql +5 -0
- sql_benchmarks/scripts/sql/null_sentinel/duckdb/2VL_identity.sql +14 -0
- sql_benchmarks/scripts/sql/null_sentinel/duckdb/3VL_identity.sql +5 -0
- sql_benchmarks/scripts/sql/null_sentinel/duckdb/sentinel_optimization.sql +16 -0
- sql_benchmarks/scripts/sql/null_sentinel/postgres/2VL_identity.sql +14 -0
- sql_benchmarks/scripts/sql/null_sentinel/postgres/3VL_identity.sql +5 -0
- sql_benchmarks/scripts/sql/null_sentinel/postgres/sentinel_optimization.sql +21 -0
- sql_benchmarks/scripts/sql/recursion/duckdb/recursive_cte.sql +26 -0
- sql_benchmarks/scripts/sql/recursion/postgres/recursive_cte.sql +30 -0
- sql_benchmarks/scripts/sql/recursive_graph/duckdb/q_bounded_3hop.sql +17 -0
- sql_benchmarks/scripts/sql/recursive_graph/duckdb/q_fixed_2hop.sql +6 -0
- sql_benchmarks/scripts/sql/recursive_graph/duckdb/q_transitive_closure.sql +17 -0
- sql_benchmarks/scripts/sql/recursive_graph/typedb/q_bounded_3hop.sql +6 -0
- sql_benchmarks/scripts/sql/recursive_graph/typedb/q_fixed_2hop.sql +5 -0
- sql_benchmarks/scripts/sql/recursive_graph/typedb/q_transitive_closure.sql +7 -0
- sql_benchmarks/scripts/sql/security/acid_resilience/duckdb/test.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_0_1_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_10_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_1_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_20_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_5_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/duckdb/q_filler.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_0_1_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_10_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_1_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_20_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_5_percent.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/postgres/q_filler.sql +1 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_0_1_percent.sql +4 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_10_percent.sql +4 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_1_percent.sql +4 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_20_percent.sql +4 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_5_percent.sql +4 -0
- sql_benchmarks/scripts/sql/selectivity/typedb/q_filler.sql +4 -0
- sql_benchmarks/scripts/sql/sort_spill/duckdb/sort_full.sql +25 -0
- sql_benchmarks/scripts/sql/sort_spill/duckdb/sort_wide_rows.sql +25 -0
- sql_benchmarks/scripts/sql/sort_spill/postgres/sort_full.sql +25 -0
- sql_benchmarks/scripts/sql/sort_spill/postgres/sort_wide_rows.sql +26 -0
- sql_benchmarks/scripts/sql/tpch/duckdb/q3_shipping_priority.sql +25 -0
- sql_benchmarks/scripts/sql/tpch/postgres/q3_shipping_priority.sql +25 -0
- sql_benchmarks/utils/__init__.py +0 -0
- sql_benchmarks/utils/common.py +260 -0
- sql_benchmarks/utils/ddl.py +47 -0
- sql_benchmarks/utils/hasher.py +124 -0
- sql_benchmarks/utils/integrity_monitor.py +55 -0
- sql_benchmarks/utils/providers.py +184 -0
- sql_benchmarks/utils/scaling.py +88 -0
- sql_benchmarks/utils/schema.py +118 -0
- sql_benchmarks/utils/semantic_auditor.py +81 -0
- sql_benchmarks/utils/system.py +100 -0
- sql_benchmarks/validator.py +60 -0
- sqlbenchdag-0.1.0.dist-info/METADATA +221 -0
- sqlbenchdag-0.1.0.dist-info/RECORD +153 -0
- sqlbenchdag-0.1.0.dist-info/WHEEL +5 -0
- sqlbenchdag-0.1.0.dist-info/entry_points.txt +2 -0
- sqlbenchdag-0.1.0.dist-info/licenses/LICENSE +202 -0
- sqlbenchdag-0.1.0.dist-info/top_level.txt +4 -0
- venv/bin/activate_this.py +59 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- analytical_wall.sql (Actian)
|
|
2
|
+
SELECT
|
|
3
|
+
region,
|
|
4
|
+
category,
|
|
5
|
+
COUNT(*) as transaction_count,
|
|
6
|
+
ROUND(CAST(SUM(price * quantity * (1 - discount)) AS numeric), 2) as net_revenue,
|
|
7
|
+
ROUND(CAST(AVG(price * quantity * (1 - discount)) AS numeric), 2) as avg_transaction_value,
|
|
8
|
+
ROUND(CAST(SUM(CASE WHEN discount > 0.15 THEN price * quantity ELSE 0 END) / SUM(price * quantity + 0.00001) * 100 AS numeric), 2) as high_discount_impact_pct
|
|
9
|
+
FROM
|
|
10
|
+
{{ analytical_data_table }}
|
|
11
|
+
GROUP BY
|
|
12
|
+
region,
|
|
13
|
+
category
|
|
14
|
+
ORDER BY
|
|
15
|
+
net_revenue DESC;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- analytical_wall.sql
|
|
2
|
+
-- Goal: Demonstrate Actian's X100 Vectorized Engine vs. Postgres Row-based Execution.
|
|
3
|
+
-- Scenario: Calculating Weighted Net Revenue Across Regions and Categories for 10M+ rows.
|
|
4
|
+
|
|
5
|
+
SELECT
|
|
6
|
+
region,
|
|
7
|
+
category,
|
|
8
|
+
COUNT(*) as transaction_count,
|
|
9
|
+
ROUND(SUM(price * quantity * (1 - discount)), 2) as net_revenue,
|
|
10
|
+
ROUND(AVG(price * quantity * (1 - discount)), 2) as avg_transaction_value,
|
|
11
|
+
-- Complex aggregation that forces per-row math
|
|
12
|
+
ROUND(SUM(CASE WHEN discount > 0.15 THEN price * quantity ELSE 0 END) / SUM(price * quantity + 0.00001) * 100, 2) as high_discount_impact_pct
|
|
13
|
+
FROM
|
|
14
|
+
analytical_data
|
|
15
|
+
GROUP BY
|
|
16
|
+
region,
|
|
17
|
+
category
|
|
18
|
+
ORDER BY
|
|
19
|
+
net_revenue DESC;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- analytical_wall.sql (DuckDB)
|
|
2
|
+
SELECT
|
|
3
|
+
region,
|
|
4
|
+
category,
|
|
5
|
+
COUNT(*) as transaction_count,
|
|
6
|
+
ROUND(SUM(price * quantity * (1 - discount)), 2) as net_revenue,
|
|
7
|
+
ROUND(AVG(price * quantity * (1 - discount)), 2) as avg_transaction_value,
|
|
8
|
+
ROUND(SUM(CASE WHEN discount > 0.15 THEN price * quantity ELSE 0 END) / SUM(price * quantity + 0.00001) * 100, 2) as high_discount_impact_pct
|
|
9
|
+
FROM
|
|
10
|
+
{{ analytical_data_table }}
|
|
11
|
+
GROUP BY
|
|
12
|
+
region,
|
|
13
|
+
category
|
|
14
|
+
ORDER BY
|
|
15
|
+
net_revenue DESC;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- analytical_wall.sql (Postgres)
|
|
2
|
+
SELECT
|
|
3
|
+
region,
|
|
4
|
+
category,
|
|
5
|
+
COUNT(*) as transaction_count,
|
|
6
|
+
ROUND(CAST(SUM(price * quantity * (1 - discount)) AS numeric), 2) as net_revenue,
|
|
7
|
+
ROUND(CAST(AVG(price * quantity * (1 - discount)) AS numeric), 2) as avg_transaction_value,
|
|
8
|
+
ROUND(CAST(SUM(CASE WHEN discount > 0.15 THEN price * quantity ELSE 0 END) / SUM(price * quantity + 0.00001) * 100 AS numeric), 2) as high_discount_impact_pct
|
|
9
|
+
FROM
|
|
10
|
+
{{ analytical_data_table }}
|
|
11
|
+
GROUP BY
|
|
12
|
+
region,
|
|
13
|
+
category
|
|
14
|
+
ORDER BY
|
|
15
|
+
net_revenue DESC;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
SELECT
|
|
2
|
+
c.customer_id,
|
|
3
|
+
c.customer_name,
|
|
4
|
+
c.region,
|
|
5
|
+
SUM(o.amount) AS total_spend
|
|
6
|
+
FROM
|
|
7
|
+
{{ customers_table }} c
|
|
8
|
+
INNER JOIN
|
|
9
|
+
{{ orders_table }} o ON c.customer_id = o.customer_id
|
|
10
|
+
GROUP BY
|
|
11
|
+
c.customer_id, c.customer_name, c.region; -- The anti-pattern
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- The Recommended CTE Pattern: Aggregate the large table first, then join.
|
|
2
|
+
WITH CustomerSpend AS (
|
|
3
|
+
SELECT
|
|
4
|
+
customer_id,
|
|
5
|
+
SUM(amount) AS total_customer_spend
|
|
6
|
+
FROM
|
|
7
|
+
{{ orders_table }}
|
|
8
|
+
GROUP BY
|
|
9
|
+
customer_id
|
|
10
|
+
)
|
|
11
|
+
SELECT
|
|
12
|
+
c.customer_id,
|
|
13
|
+
c.customer_name,
|
|
14
|
+
c.region,
|
|
15
|
+
cs.total_customer_spend
|
|
16
|
+
FROM
|
|
17
|
+
{{ customers_table }} c
|
|
18
|
+
INNER JOIN
|
|
19
|
+
CustomerSpend cs ON c.customer_id = cs.customer_id;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- groupby_anti_pattern.sql
|
|
2
|
+
SELECT
|
|
3
|
+
c.customer_id,
|
|
4
|
+
c.customer_name,
|
|
5
|
+
c.region,
|
|
6
|
+
SUM(o.amount) AS total_amount
|
|
7
|
+
FROM
|
|
8
|
+
{{ customers_table }} c
|
|
9
|
+
INNER JOIN
|
|
10
|
+
{{ orders_table }} o ON c.customer_id = o.customer_id
|
|
11
|
+
GROUP BY
|
|
12
|
+
c.customer_id, c.customer_name, c.region;
|
|
13
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
SELECT s.id AS supplier_id, s.country,
|
|
2
|
+
b.id AS buyer_id, b.region,
|
|
3
|
+
p.id AS product_id, p.category
|
|
4
|
+
FROM {{ supply_contract_table }} sc
|
|
5
|
+
JOIN {{ supplier_table }} s ON s.id = sc.supplier_id
|
|
6
|
+
JOIN {{ buyer_table }} b ON b.id = sc.buyer_id
|
|
7
|
+
JOIN {{ product_table }} p ON p.id = sc.product_id
|
|
8
|
+
WHERE s.country = 'US'
|
|
9
|
+
AND b.region = 'APAC';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- 1. Matches for Non-Nulls (Uses Hash Join)
|
|
2
|
+
SELECT count(*)
|
|
3
|
+
FROM {{ r_table }} r1
|
|
4
|
+
JOIN {{ r_table }} r2
|
|
5
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }}
|
|
6
|
+
|
|
7
|
+
UNION ALL
|
|
8
|
+
|
|
9
|
+
-- 2. Matches for Nulls (Identity)
|
|
10
|
+
-- This is just a cartesian of nulls, but isolated so it doesn't kill the Hash Join
|
|
11
|
+
SELECT count(*)
|
|
12
|
+
FROM {{ r_table }} r1
|
|
13
|
+
JOIN {{ r_table }} r2
|
|
14
|
+
ON r1.{{ join_key_a }} IS NULL AND r2.{{ join_key_a }} IS NULL;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- 1. Matches for Non-Nulls (Uses Hash Join)
|
|
2
|
+
SELECT count(*)
|
|
3
|
+
FROM {{ r_table }} r1
|
|
4
|
+
JOIN {{ r_table }} r2
|
|
5
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }}
|
|
6
|
+
|
|
7
|
+
UNION ALL
|
|
8
|
+
|
|
9
|
+
-- 2. Matches for Nulls (Identity)
|
|
10
|
+
-- This is just a cartesian of nulls, but isolated so it doesn't kill the Hash Join
|
|
11
|
+
SELECT count(*)
|
|
12
|
+
FROM {{ r_table }} r1
|
|
13
|
+
JOIN {{ r_table }} r2
|
|
14
|
+
ON r1.{{ join_key_a }} IS NULL AND r2.{{ join_key_a }} IS NULL;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- 1. Matches for Non-Nulls (Uses Hash Join)
|
|
2
|
+
SELECT count(*)
|
|
3
|
+
FROM {{ r_table }} r1
|
|
4
|
+
JOIN {{ r_table }} r2
|
|
5
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }}
|
|
6
|
+
|
|
7
|
+
UNION ALL
|
|
8
|
+
|
|
9
|
+
-- 2. Matches for Nulls (Identity)
|
|
10
|
+
-- This is just a cartesian of nulls, but isolated so it doesn't kill the Hash Join
|
|
11
|
+
SELECT count(*)
|
|
12
|
+
FROM {{ r_table }} r1
|
|
13
|
+
JOIN {{ r_table }} r2
|
|
14
|
+
ON r1.{{ join_key_a }} IS NULL AND r2.{{ join_key_a }} IS NULL;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Benchmarks the cost of handling NULLs as a concrete value ("Identity")
|
|
2
|
+
-- vs letting the database skip them (Standard 3VL).
|
|
3
|
+
DROP TABLE IF EXISTS {{ r_table }}_sentinel;
|
|
4
|
+
|
|
5
|
+
CREATE TABLE {{ r_table }}_sentinel AS
|
|
6
|
+
SELECT
|
|
7
|
+
COALESCE({{ join_key_a }}, -1) AS {{ join_key_a }}
|
|
8
|
+
FROM {{ r_table }};
|
|
9
|
+
|
|
10
|
+
ANALYZE {{ r_table }}_sentinel;
|
|
11
|
+
|
|
12
|
+
SELECT count(*)
|
|
13
|
+
FROM {{ r_table }}_sentinel r1
|
|
14
|
+
JOIN {{ r_table }}_sentinel r2
|
|
15
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }};
|
|
16
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- 1. Matches for Non-Nulls (Uses Hash Join)
|
|
2
|
+
SELECT count(*)
|
|
3
|
+
FROM {{ r_table }} r1
|
|
4
|
+
JOIN {{ r_table }} r2
|
|
5
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }}
|
|
6
|
+
|
|
7
|
+
UNION ALL
|
|
8
|
+
|
|
9
|
+
-- 2. Matches for Nulls (Identity)
|
|
10
|
+
-- This is just a cartesian of nulls, but isolated so it doesn't kill the Hash Join
|
|
11
|
+
SELECT count(*)
|
|
12
|
+
FROM {{ r_table }} r1
|
|
13
|
+
JOIN {{ r_table }} r2
|
|
14
|
+
ON r1.{{ join_key_a }} IS NULL AND r2.{{ join_key_a }} IS NULL;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Benchmarks the cost of handling NULLs as a concrete value ("Identity")
|
|
2
|
+
-- vs letting the database skip them (Standard 3VL).
|
|
3
|
+
-- [PREP_START]
|
|
4
|
+
DROP TABLE IF EXISTS {{ r_table }}_sentinel;
|
|
5
|
+
|
|
6
|
+
-- 1. Physical Materialization (The "Baking" Phase)
|
|
7
|
+
CREATE UNLOGGED TABLE {{ r_table }}_sentinel AS
|
|
8
|
+
SELECT
|
|
9
|
+
COALESCE({{ join_key_a }}, -1) AS {{ join_key_a }}
|
|
10
|
+
FROM {{ r_table }};
|
|
11
|
+
|
|
12
|
+
-- 2. Statistic Update (The "Notification" Phase)
|
|
13
|
+
ANALYZE {{ r_table }}_sentinel;
|
|
14
|
+
-- [PREP_END]
|
|
15
|
+
|
|
16
|
+
-- 3. The Benchmark
|
|
17
|
+
SELECT count(*)
|
|
18
|
+
FROM {{ r_table }}_sentinel r1
|
|
19
|
+
JOIN {{ r_table }}_sentinel r2
|
|
20
|
+
ON r1.{{ join_key_a }} = r2.{{ join_key_a }};
|
|
21
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
WITH RECURSIVE
|
|
2
|
+
-- {{ hierarchy_table }} will be rendered as `hierarchy_partitionkey` (e.g., hierarchy_small)
|
|
3
|
+
descendants (id, parent_id, name, depth) AS (
|
|
4
|
+
SELECT
|
|
5
|
+
id,
|
|
6
|
+
parent_id,
|
|
7
|
+
name,
|
|
8
|
+
1 AS depth
|
|
9
|
+
FROM {{ hierarchy_data_table }}
|
|
10
|
+
WHERE id = 1 -- Start at a known root/branch node
|
|
11
|
+
|
|
12
|
+
UNION ALL
|
|
13
|
+
|
|
14
|
+
SELECT
|
|
15
|
+
t.id,
|
|
16
|
+
t.parent_id,
|
|
17
|
+
t.name,
|
|
18
|
+
d.depth + 1
|
|
19
|
+
FROM {{ hierarchy_data_table }} AS t
|
|
20
|
+
INNER JOIN descendants AS d
|
|
21
|
+
ON t.parent_id = d.id
|
|
22
|
+
)
|
|
23
|
+
SELECT
|
|
24
|
+
-- The final result is the count of all descendant nodes found
|
|
25
|
+
count(*)
|
|
26
|
+
FROM descendants;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- sql_queries/postgres/recursion_cte.sql
|
|
2
|
+
-- Recursively finds all descendants (or ancestors) of a specific node.
|
|
3
|
+
-- We use node ID 1 as a common starting point.
|
|
4
|
+
|
|
5
|
+
WITH RECURSIVE
|
|
6
|
+
-- {{ hierarchy_table }} will be rendered as `hierarchy_partitionkey`
|
|
7
|
+
descendants (id, parent_id, name, depth) AS (
|
|
8
|
+
SELECT
|
|
9
|
+
id,
|
|
10
|
+
parent_id,
|
|
11
|
+
name,
|
|
12
|
+
1 AS depth
|
|
13
|
+
FROM {{ hierarchy_data_table }} AS t
|
|
14
|
+
WHERE id = 1 -- Start at a known root/branch node
|
|
15
|
+
|
|
16
|
+
UNION ALL
|
|
17
|
+
|
|
18
|
+
SELECT
|
|
19
|
+
t.id,
|
|
20
|
+
t.parent_id,
|
|
21
|
+
t.name,
|
|
22
|
+
d.depth + 1
|
|
23
|
+
FROM {{ hierarchy_data_table }} AS t
|
|
24
|
+
INNER JOIN descendants AS d
|
|
25
|
+
ON t.parent_id = d.id
|
|
26
|
+
)
|
|
27
|
+
SELECT
|
|
28
|
+
-- The final result is the count of all descendant nodes found
|
|
29
|
+
count(*)
|
|
30
|
+
FROM descendants;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Bounded variable-depth: all companies reachable from node 1 in up to 3 hops.
|
|
2
|
+
-- Recursive CTE with depth guard. Intermediate result set materialised at each
|
|
3
|
+
-- step — this is where OOM occurs on hub-heavy Zipf graphs.
|
|
4
|
+
WITH RECURSIVE reachable(company_id, depth) AS (
|
|
5
|
+
SELECT to_id AS company_id, 1 AS depth
|
|
6
|
+
FROM {{ supplies_table }}
|
|
7
|
+
WHERE from_id = 1
|
|
8
|
+
|
|
9
|
+
UNION
|
|
10
|
+
|
|
11
|
+
SELECT s.to_id, r.depth + 1
|
|
12
|
+
FROM reachable r
|
|
13
|
+
JOIN {{ supplies_table }} s ON s.from_id = r.company_id
|
|
14
|
+
WHERE r.depth < 3
|
|
15
|
+
)
|
|
16
|
+
SELECT COUNT(DISTINCT company_id) AS reachable_count
|
|
17
|
+
FROM reachable;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- Fixed 2-hop: companies reachable from node 1 in exactly 2 supply steps.
|
|
2
|
+
-- Baseline — DuckDB should win comfortably. Two plain JOINs on integer FKs.
|
|
3
|
+
SELECT DISTINCT s2.to_id AS company_id
|
|
4
|
+
FROM {{ supplies_table }} s1
|
|
5
|
+
JOIN {{ supplies_table }} s2 ON s1.to_id = s2.from_id
|
|
6
|
+
WHERE s1.from_id = 1;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Full transitive closure from node 1: every company that directly or
|
|
2
|
+
-- indirectly depends on node 1 in the supply chain.
|
|
3
|
+
-- No depth limit — the CTE runs until no new nodes are discovered.
|
|
4
|
+
-- This is the query most likely to OOM on a Zipf hub at medium scale.
|
|
5
|
+
WITH RECURSIVE closure(company_id) AS (
|
|
6
|
+
SELECT to_id AS company_id
|
|
7
|
+
FROM {{ supplies_table }}
|
|
8
|
+
WHERE from_id = 1
|
|
9
|
+
|
|
10
|
+
UNION
|
|
11
|
+
|
|
12
|
+
SELECT s.to_id
|
|
13
|
+
FROM closure c
|
|
14
|
+
JOIN {{ supplies_table }} s ON s.from_id = c.company_id
|
|
15
|
+
)
|
|
16
|
+
SELECT COUNT(DISTINCT company_id) AS total_reachable
|
|
17
|
+
FROM closure;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
match
|
|
2
|
+
$root isa {{ company_table }}, has id 1;
|
|
3
|
+
(seller_role: $root, buyer_role: $hop1) isa {{ supplies_table }};
|
|
4
|
+
(seller_role: $hop1, buyer_role: $hop2) isa {{ supplies_table }};
|
|
5
|
+
(seller_role: $hop2, buyer_role: $leaf) isa {{ supplies_table }};
|
|
6
|
+
select $hop1, $hop2, $leaf;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Full transitive closure from node 1 using a TypeDB 3.x recursive stream function.
|
|
2
|
+
# The 'reachable' function is defined in the schema and evaluated lazily with tabling
|
|
3
|
+
# (automatic cycle detection / fixed-point termination). No explicit loop needed.
|
|
4
|
+
match
|
|
5
|
+
$root isa {{ company_table }}, has id 1;
|
|
6
|
+
let $reachable in reachable($root);
|
|
7
|
+
select $reachable;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT COUNT(*) FROM heartbeat;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_0_1';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_10';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_1';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_20';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_5';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'filler';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_0_1';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_10';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_1';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_20';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'sel_5';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT count(*) FROM {{ skewed_data_table }} WHERE selectivity_code = 'filler';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Sort Spill: Full sort (no LIMIT) on wide rows (DuckDB control).
|
|
2
|
+
-- DuckDB uses vectorized execution and adaptive memory management.
|
|
3
|
+
-- No work_mem equivalent — spill is handled transparently.
|
|
4
|
+
-- Compare against postgres/sort_full.sql: without top-N optimization,
|
|
5
|
+
-- the full architectural cost of row-at-a-time vs vectorized sort is exposed.
|
|
6
|
+
|
|
7
|
+
SELECT
|
|
8
|
+
id,
|
|
9
|
+
region,
|
|
10
|
+
category,
|
|
11
|
+
department,
|
|
12
|
+
sub_category,
|
|
13
|
+
ROUND(price, 4) AS price,
|
|
14
|
+
ROUND(cost, 4) AS cost,
|
|
15
|
+
ROUND(margin, 4) AS margin,
|
|
16
|
+
quantity,
|
|
17
|
+
discount,
|
|
18
|
+
ROUND(price * quantity * (1.0 - discount), 4) AS net_revenue
|
|
19
|
+
FROM {{ sort_data_table }}
|
|
20
|
+
ORDER BY
|
|
21
|
+
region ASC,
|
|
22
|
+
category ASC,
|
|
23
|
+
department ASC,
|
|
24
|
+
net_revenue DESC,
|
|
25
|
+
quantity DESC;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Sort Spill: Multi-column sort on wide rows (DuckDB control).
|
|
2
|
+
-- DuckDB manages memory adaptively — no work_mem equivalent.
|
|
3
|
+
-- This is the control arm: same query, no spill constraint.
|
|
4
|
+
-- Compare against postgres/sort_wide_rows.sql to isolate the spill cost.
|
|
5
|
+
|
|
6
|
+
SELECT
|
|
7
|
+
id,
|
|
8
|
+
region,
|
|
9
|
+
category,
|
|
10
|
+
department,
|
|
11
|
+
sub_category,
|
|
12
|
+
ROUND(price, 4) AS price,
|
|
13
|
+
ROUND(cost, 4) AS cost,
|
|
14
|
+
ROUND(margin, 4) AS margin,
|
|
15
|
+
quantity,
|
|
16
|
+
discount,
|
|
17
|
+
ROUND(price * quantity * (1.0 - discount), 4) AS net_revenue
|
|
18
|
+
FROM {{ sort_data_table }}
|
|
19
|
+
ORDER BY
|
|
20
|
+
region ASC,
|
|
21
|
+
category ASC,
|
|
22
|
+
department ASC,
|
|
23
|
+
net_revenue DESC,
|
|
24
|
+
quantity DESC
|
|
25
|
+
LIMIT 1000;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Sort Spill: Full sort (no LIMIT) on wide rows.
|
|
2
|
+
-- Without LIMIT, Postgres cannot use the top-N heap sort optimization.
|
|
3
|
+
-- The entire result set must be sorted in memory or spilled to disk.
|
|
4
|
+
-- At tight work_mem (4MB), spill will occur at low row counts.
|
|
5
|
+
-- This is where the cliff actually lives.
|
|
6
|
+
|
|
7
|
+
SELECT
|
|
8
|
+
id,
|
|
9
|
+
region,
|
|
10
|
+
category,
|
|
11
|
+
department,
|
|
12
|
+
sub_category,
|
|
13
|
+
ROUND(CAST(price AS NUMERIC), 4) AS price,
|
|
14
|
+
ROUND(CAST(cost AS NUMERIC), 4) AS cost,
|
|
15
|
+
ROUND(CAST(margin AS NUMERIC), 4) AS margin,
|
|
16
|
+
quantity,
|
|
17
|
+
discount,
|
|
18
|
+
ROUND(CAST(price * quantity * (1.0 - discount) AS NUMERIC), 4) AS net_revenue
|
|
19
|
+
FROM {{ sort_data_table }}
|
|
20
|
+
ORDER BY
|
|
21
|
+
region ASC,
|
|
22
|
+
category ASC,
|
|
23
|
+
department ASC,
|
|
24
|
+
net_revenue DESC,
|
|
25
|
+
quantity DESC;
|